/* ============================================================
   Shared homepage section patterns
   Import in any section component that uses the sticky badge rail.

   Provides: .hp-section shell, .hp-rail sticky rail,
   .hp-badge pill, .hp-heading, .hp-body
   ============================================================ */


/* ---- Section shell ----------------------------------------
   Background-color is intentionally omitted — each section
   sets its own via a single-rule override. */

.hp-section {
  position:       relative;
  display:        flex;
  flex-direction: column;
  align-items:    center;
  gap:            4rem;      /* 64px */
  padding:        8rem 0;   /* 128px top + bottom */
  font-family:    var(--font-family-default);
  width:          100%;
  box-sizing:     border-box;

}

/* Tablet (≤991px) */
@media (max-width: 991px) {
  .hp-section {
    padding: 6rem 0;
    gap:     3rem;
  }
}

/* Mobile (≤767px) */
@media (max-width: 767px) {
  .hp-section {
    padding: 4rem 0;
    gap:     2rem;
  }
}

/* ---- Tight modifier -----------------------------------------
   Used by the AIR product page, where the default 128px rhythm reads
   as too loose. Same 0.75 / 0.5 tablet/mobile scaling as .hp-section
   above, just off a 96px desktop baseline instead of 128px. Add
   alongside .hp-section (e.g. className="hp-section hp-section--tight
   air-cta"); a section can still override further for anything that
   doesn't fit (see AirHowItWorksSection's mobile padding). */
.hp-section--tight {
  padding-block: 6rem;   /* 96px */
}

@media (max-width: 991px) {
  .hp-section--tight {
    padding-block: 4.5rem;   /* 72px */
  }
}

@media (max-width: 767px) {
  .hp-section--tight {
    padding-block: 3rem;   /* 48px */
  }
}


/* ---- Sticky badge rail ------------------------------------
   Absolutely positioned inside the first .container, pushed
   left via right: calc(100% - 80px).  The offset skips the
   container's left padding so the rail anchors to the content
   edge rather than the padding edge.  .container has
   position: relative so the rail is bounded to the container's
   height — badge stops when the container ends.
   Width = gutter between container (1024px) and container-wide
   (1384px): (1384 − 1024) / 2 = 180px.
   Shown only at ≥1480px — when container-wide is at max-width
   and the gutter is a fixed, predictable size. */

.hp-rail {
  display:        none;
  position:       absolute;
  right:          calc(100% - 80px);   /* 80px = container left padding */
  top:            0;
  bottom:         0;
  width:          calc((1384px - 1024px) / 2);   /* 180px */
  pointer-events: none;
}

.hp-rail__sticky {
  position:        sticky;
  top:             var(--nav-section-offset, 8rem);
  display:         flex;
  justify-content: flex-end;
  align-items:     flex-start;
  padding-right:   12px;   /* 12px gap from the container's left edge */
}

@media (min-width: 1480px) {
  .hp-rail {
    display:         flex;
    justify-content: flex-start;
    align-items:     flex-start;
  }

  .container[data-hp-rail-overflow] > .hp-rail {
    visibility: hidden;
  }
}


/* ---- Badge pill -------------------------------------------
   Base pill used in the sticky rail and as the inline badge.
   .hp-badge--inline controls visibility relative to breakpoint. */

.hp-badge {
  display:         inline-flex;
  align-items:     center;
  justify-content: center;
  gap:             6px;
  height:          26px;
  padding:         0 var(--spacing-3);
  background:      var(--card);
  border-radius:   var(--radius-full);
  font-size:       var(--font-size-xs);
  font-weight:     var(--font-weight-medium);
  line-height:     var(--line-height-xs);
  color:           var(--foreground);
  white-space:     nowrap;
}

/* Inline variant — shown below 1480px, hidden at and above (matches the rail
   breakpoint so there's never a gap where neither badge shows).

   It carries NO margin: `.section-header` owns badge→heading spacing via its
   24px `gap`. The one exception is a header that deliberately wants margin-
   driven spacing (DemoSection, `gap: 0`), which adds its own
   `.x .hp-badge--inline { margin-bottom }`. */
@media (min-width: 1480px) {
  .hp-badge--inline {
    display: none;
  }

  .container[data-hp-rail-overflow] .hp-badge--inline {
    display: inline-flex;
  }
}

/* Live indicator dot (DemoSection "Live demo" badge) */
.hp-badge__dot {
  display:          block;
  width:            6px;
  height:           6px;
  border-radius:    var(--radius-full);
  background-color: var(--primary);
  flex-shrink:      0;
}

/* ---- Chip variant -----------------------------------------
   A badge with a leading highlight chip + label, e.g.
   "New · Quality Studio". Put .hp-badge--chip on the badge and
   .hp-badge__chip on the leading element. Works in both the
   sticky rail (via <SectionRail className="hp-badge--chip">) and
   the inline position. */

.hp-badge--chip {
  gap:     4px;
  padding: 4px 12px 4px 4px;   /* py-4, pr-12, tight pl-4 so the chip hugs the edge */
}

.hp-badge__chip {
  display:       inline-flex;
  align-items:   center;
  height:        100%;
  padding:       0 8px;
  border-radius: var(--radius-full);
  background:    var(--opacity-primary-20);
  color:         var(--primary);
}


/* ---- Section header --------------------------------------
   Sitewide convention for the badge + heading + body block at
   the top of a section. Use directly (do not clone into a
   per-section class). The inline badge is the first child; the
   heading+body live in `__text`. Spacing is owned here so it is
   identical across every section:
     badge → 24px → text → 36px (heading→body) → 24px → actions

   align-items: flex-start is what keeps the inline badge from
   stretching full-width on small screens. */

.section-header {
  display:        flex;
  flex-direction: column;
  align-items:    flex-start;
  gap:            var(--spacing-6);   /* 24px — owns badge→heading spacing */
}

.section-header__text {
  display:        flex;
  flex-direction: column;
  align-items:    flex-start;         /* don't stretch children — buttons keep their
                                         natural width (paragraphs still wrap at max-width) */
  gap:            var(--spacing-9);   /* 36px */
  max-width:      50rem;              /* 800px readable measure (heading + body) */
}

/* The body keeps the readable measure even when the block is widened (--wide),
   so a wide heading never drags the body to full width. This is the universal
   cap — sections should NOT add their own `.x .hp-body { max-width }`. */
.section-header__text .hp-body {
  max-width: 50rem;
}

/* Modifiers */
.section-header--center {
  align-items: center;
  text-align:  center;
}
/* Heading uses the full width; the body stays capped by the rule above. */
.section-header__text--wide {
  max-width: none;
}

.section-header__text--gap-36 {
  gap: var(--spacing-9);
}

@media (max-width: 767px) {
  .section-header       { gap: var(--spacing-4); }   /* 16px */
  .section-header__text { gap: var(--spacing-6); }   /* 24px */
  .section-header__text--gap-36 { gap: var(--spacing-9); }   /* 36px */
}


/* ---- Section intro row ------------------------------------
   Full-width text-left / action-right row. The copy slot expands to push
   actions to the far edge; text inside keeps the standard 50rem measure. */

.section-intro-row {
  display:     flex;
  align-items: center;
  gap:         2.25rem;   /* 36px */
  width:       100%;
}

.section-intro-row__copy {
  flex:      1;
  min-width: 0;
}

.section-intro-row__copy > .hp-body {
  max-width: 50rem;
}

.section-intro-row__actions {
  flex-shrink: 0;
}

@media (max-width: 767px) {
  .section-intro-row {
    flex-direction: column;
    align-items:    flex-start;
    gap:            2.25rem;   /* 36px */
  }
}


/* ---- Section heading --------------------------------------
   36px regular weight; .fw-bold inside uses bold. */

.hp-heading {
  margin:      0;
  font-size:   2.25rem;
  font-weight: var(--font-weight-regular);
  line-height: 2.5rem;
  color:       var(--foreground);
}

.hp-heading.fw-bold {
  font-weight: var(--font-weight-bold);
}



/* ---- Section subheading -----------------------------------
   24px — used beneath headings as a secondary label. */

.hp-subheading {
  margin:      0;
  font-size:   1.5rem;
  line-height: 2rem;
  color:       var(--foreground);
}


/* ---- Section body text ------------------------------------
   20px regular — used in header intro blocks. */

.hp-body {
  margin:      0;
  font-size:   1.25rem;
  line-height: 1.75rem;
  color:       var(--foreground);
}

@media (max-width: 767px) {
  .hp-heading {
    font-size:   1.875rem;  /* 30px */
    line-height: 2.25rem;   /* 36px */
  }

  .hp-subheading {
    font-size:   1.25rem;   /* 20px */
    line-height: 1.75rem;   /* 28px */
  }

  .hp-body {
    font-size:   1.125rem;  /* 18px */
    line-height: 1.75rem;   /* 28px */
  }
}


/* ---- Shared card shell ------------------------------------
   Secondary background + hairline border + 12px radius.
   Use alongside component class for layout-specific rules. */

.card-secondary {
  background:    var(--secondary);
  border:        0.5px solid var(--border);
  border-radius: 12px;
  box-sizing:    border-box;
}


/* ---- 26px number pill -------------------------------------
   Circular badge used for step numbers.
   Color (background) set per-component. */

.num-pill {
  display:         inline-flex;
  align-items:     center;
  justify-content: center;
  width:           26px;
  height:          26px;
  flex-shrink:     0;
  border-radius:   var(--radius-full);
  font-size:       var(--font-size-xs);
  font-weight:     var(--font-weight-medium);
  line-height:     1;
  color:           var(--foreground);
}


/* ---- Card typography --------------------------------------
   Base/bold title and base/regular muted body.
   Color overrides applied per-component as needed. */

.card-title {
  margin:      0;
  font-size:   var(--font-size-base);
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-base);
}

.card-body {
  margin:      0;
  font-size:   var(--font-size-base);
  font-weight: var(--font-weight-regular);
  line-height: var(--line-height-base);
  color:       var(--muted-foreground);
}


/* ---- Page H1 — shared display heading --------------------- */

.page-h1 {
  margin:      0;
  font-size:   3rem;    /* 48px */
  line-height: 1.15;
  font-weight: var(--font-weight-light);
  color:       var(--foreground);
}

.page-h1__line {
  display: block;
}

@media (max-width: 767px) {
  .page-h1 { font-size: 2.25rem; }
}

@media (max-width: 479px) {
  .page-h1 { font-size: 2rem; }

  /* Opt-out of the portrait-mobile shrink: holds the ≤767 size (36px) below
     480px. Add alongside `.page-h1` when a mobile Figma frame keeps the larger
     headline (e.g. AirHeroSection). Replicate the pattern for other heading
     classes if/when the same need arises. */
  .page-h1--no-portrait-shrink { font-size: 2.25rem; }
}


/* ---- Button pair — wrap row + content-driven equal width ---
   A two-(or-more)-button CTA row that never clips or overflows: flex-wrap
   means it stacks instead of clipping when space runs out. Pair with the
   `buttonPairStyle()` helper (src/utils/buttonPair.ts), which sets
   --btn-pair-min-w from the longest label's own character count (a `ch`
   value — scales with the font) — NOT a guessed pixel breakpoint. min-width
   only ever WIDENS a button toward the longest one; it can never shrink a
   button below its own text, so there's no clipping risk either way.

   Applies the equal-ish-width treatment at every size by default. To scope
   it to one breakpoint only (e.g. mobile-only, like AirCtaSection), add a
   local override in the consuming component's CSS:
     .my-actions .btn { min-width: 0; }                          // reset
     @media (max-width: 767px) {
       .my-actions .btn { min-width: var(--btn-pair-min-w, 0); } // re-enable
     }
   Full writeup: docs/shared-css-classes.md → "Button pair". */

.btn-pair {
  display:   flex;
  flex-wrap: wrap;
  gap:       0.75rem;   /* 12px */
}

.btn-pair .btn {
  min-width: var(--btn-pair-min-w, 0);
}


/* ---- Grained ground band ----------------------------------
   ONE element, painted once behind a whole page block.

   Figma draws the ground as a single rect (946:8415, 2571x725) that starts
   part-way down the Legal Hub hero and runs to the bottom of the social-proof
   block. It is rendered here as one absolutely-positioned band inside the
   wrapper that spans both sections (LegalHubHeroBlock) — NOT sliced across the
   two section components. An earlier build did slice it (`--ground-span` /
   `--ground-offset`), and every seam it had to reconcile — a grain mask that
   ran out before the section did, a logo-reel fade painted in a flat
   approximation of the ramp — came from that split. Do not reintroduce it.

   What the band actually is, measured off the Figma render rather than assumed:
   a FLAT page-cream fill with a grain whose mask ramps transparent -> opaque.
   The apparent darkening (median 244 at the top, 229 at the bottom) is entirely
   the grain; the fill never changes. The giveaway is that Figma's brightest
   pixel is 245 — the page cream — at every depth, which a real gradient could
   not do. There is deliberately no `linear-gradient` here.

   Grain calibration, sampled from Figma's own render (R channel; median rejects
   the grain, mean row sd measures it) against this rule:

     depth   Figma med/sd     ours med/sd
        0     244 / 0.43      245 / 0.42
      160     241 / 2.43      241 / 2.27
      320     237 / 4.21      238 / 4.12
      480     234 / 6.36      234 / 5.97
      720     229 / 9.27      230 / 10.51

   `multiply` alone at the shared `.noise-grain` opacity cannot get there: this
   texture is a smooth fractal cloud, so it reaches sd 5.5 only by dragging the
   median 14 levels down — half Figma's contrast, twice its muddying. The
   `contrast()` filter widens the texture's luminance spread and `brightness()`
   puts the mean back, which buys the contrast without the mud. Multiply is
   correct as the blend: Figma's grain only ever darkens.

   The band paints its own opaque `--background`, so whatever renders it must
   sit BEHIND the block's content and the content must not paint over it. */

.grained-ground {
  position:         relative;
  background-color: var(--background);
}

.grained-ground::after {
  content:            '';
  position:           absolute;
  inset:              0;
  pointer-events:     none;
  background-image:   var(--noise-texture);
  background-repeat:  repeat;
  /* 200px is the texture's own viewBox — the size every other consumer uses. */
  background-size:    200px 200px;
  mix-blend-mode:     multiply;
  filter:             contrast(3.5) brightness(0.88);
  /* The ramp. Nothing else in this rule varies with depth. */
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000);
          mask-image: linear-gradient(to bottom, transparent, #000);
}


/* ---- Grain overlay ----------------------------------------
   ONE texture for the whole site: `the-noise.svg`, the 236-byte feTurbulence
   SVG already used by AirTrialSection, InlineBanner, LeadFormSection and
   PartnerFinalCtaSection. It is generated by the browser's SVG filter, not a
   downloaded raster, and because every consumer points at the same URL the
   browser fetches it once for the whole site.

   Do NOT add another noise file. Figma ships its grain as a 400x400 raster
   (a binary dither, ~51% opaque mid-grey); at these opacities it is
   indistinguishable from this one and costs 56 KB more, so we reuse this.
   `CaseStudiesListing` inlines the same filter as a data URI — the one
   remaining variant, left alone because it is already shipped.

   Figma applies the grain as a full-bleed sheet over the surface it grains, and
   in the flow diagram twice: once directly on the background wash under the
   content (node 865:6309) and once over everything as the frame's last child
   (865:6476).

   Usage: `noise-grain` paints the sheet ON TOP (::after); add
   `noise-grain-under` for a second sheet BENEATH the content (::before). Tune
   per surface with `--noise-opacity`. `border-radius: inherit` keeps the grain
   inside rounded and circular surfaces (the 67% pie is a circle).

   Blend, and why there are two: Figma's sheet has to darken pale fills but stay
   mean-neutral on saturated dark ones, and no single CSS blend does both.
   Both settings below were measured against Figma's own render, not guessed:

     surface        blend       opacity   ours (mean/sd)   Figma (mean/sd)
     67% pie        multiply    0.45      183.3 / 4.42     186.1 / 4.54
     100% dial      soft-light  0.60       30.5 / 2.45      30.8 / 2.25

   So: default (light fills) is `multiply`; add `noise-grain--on-dark` for
   saturated dark fills. `multiply` on the dial crushes it to 22.7 and kills the
   grain (sd 0.51); `normal` washes it out to 60.8 — hence the split.

   The four older consumers use `screen` because they grain DARK blue cards
   where the sheet has to lighten. Same texture, different direction — not a
   competing convention.

   Decorative only — never a hit target. */

:host {
  --noise-texture: url('https://cdn.prod.website-files.com/5b15ca3d02dd414c89ecc3ed/6a10ffdb2b90b09311ca7952_the-noise.svg');
}

.noise-grain,
.noise-grain-under {
  position: relative;
}

.noise-grain::after,
.noise-grain-under::before {
  content:            '';
  position:           absolute;
  inset:              0;
  pointer-events:     none;
  background-image:   var(--noise-texture);
  background-repeat:  repeat;
  /* 200px is the texture's own viewBox — the size every other consumer uses. */
  background-size:    200px 200px;
  mix-blend-mode:     var(--noise-blend, multiply);
  opacity:            var(--noise-opacity, 0.45);
  /* Optional contrast/brightness tuning. `multiply` on this texture buys
     contrast only by darkening — it is a smooth fractal cloud, not a dither —
     so a surface that needs Figma's grain WITHOUT Figma's mud sets a
     `contrast()` here and drops the opacity to compensate. Defaults to `none`,
     so every existing consumer is untouched. See `.flow-diagram`. */
  filter:             var(--noise-filter, none);
  border-radius:      inherit;
}

/* Saturated dark fills — see the table above. */
.noise-grain--on-dark {
  --noise-blend:   soft-light;
  --noise-opacity: 0.6;
}


/* ---- Orange text link -------------------------------------
   Sitewide convention for a standalone orange text link (NOT a button —
   use <Button> for those): orange at rest, darker orange on hover, never
   underlined, 0.2s color transition.

   The hover value is deliberately the SAME mix the secondary Button uses
   for its text/border hover (Button.css → [data-variant="secondary"]
   [data-tone="default"]:hover). Orange hover is one treatment in this
   library, shared by buttons and links — if that mix ever changes, change
   it in both places. Because it is derived from `var(--primary)` it also
   darkens correctly in the dark scheme with no separate override.

   Full writeup: docs/shared-css-classes.md → "Text link". */

.text-link {
  color:           var(--primary);
  text-decoration: none;
  transition:      color 0.2s;
}

.text-link:hover,
.text-link:focus-visible {
  color: color-mix(in srgb, var(--primary) 80%, #282825 20%);
}

.text-link:focus-visible {
  border-radius:  2px;
  outline:        2px solid currentColor;
  outline-offset: 2px;
}

/* Trailing-icon variant — keeps the icon on the text baseline and stops it
   being squashed when the label wraps. */
.text-link--arrow {
  display:     inline-flex;
  align-items: center;
  gap:         var(--spacing-2);   /* 8px */
}

.text-link--arrow svg {
  flex-shrink: 0;
}

