/* ================================================================
   css/pages/solutions.css — page-only CSS for /solutions.
   Sections: her, industries, processes (menu-line / footer /
   inner-annual are shared partials + shared CSS, not touched here).

   Values sourced from the inline <style> blocks in
   research/raw/{desktop,tablet,phone}/solutions.html (extracted with
   scratchpad/pagecss.mjs) — see BUILD-PLAN §"Landmines": _stylesheet.css
   only carries the HOME page's CSS, so nothing here was guessed.

   Breakpoints (reference's own, NOT 768/1024):
     desktop  >= 1440
     tablet+laptop  810 .. 1439.98
     phone    <= 809.98
   ================================================================ */

/* ----------------------------------------------------------------
   Small shared primitives, page-scoped (not worth promoting to
   base.css for a single page): the orange/mono "number" pill used by
   all three section headers, and a couple of vline colour modifiers.
   ---------------------------------------------------------------- */
.badge-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 31px;
  padding: 0 10px;
  background-color: var(--c-orange);
  flex: none;
}
.badge-pill__text { color: var(--c-white); white-space: pre; }

.vline--hair   { --border-left-width: 1px; --border-right-width: 1px; --border-color: var(--c-stone-cool-50); --border-style: solid; width: 1px; }
.vline--dash   { --border-left-width: 3px; --border-right-width: 3px; --border-color: var(--c-stone-85); --border-style: dashed; width: 1px; }
.vline--orange { --border-left-width: 3px; --border-right-width: 3px; --border-color: var(--c-peach-95); --border-style: dashed; width: 1px; }
.hline {
  position: relative;
  flex: none;
  width: 100%;
  height: 1px;
}
.hline::after {
  content: "";
  position: absolute;
  inset: 0;
  border-top: 1px solid var(--c-stone);
}
.hline--dash::after { border-top: 2px dashed var(--c-stone-85); }

/* small "+" crosshair used at a couple of grid intersections */
.plus-mark { display: block; flex: none; width: 15px; height: 15px; }
.plus-mark--orange line { stroke: var(--c-orange); }
.plus-mark--ink line    { stroke: var(--c-ink); }

/* dark CTA button — same mechanic as help.css's .btn-dark (SHARED-NOTES
   §10 "icon-in-button"); help.css is not loaded on this page (no help
   section here), so it is reproduced locally rather than importing an
   unrelated shared stylesheet just for one class. */
.btn-dark {
  cursor: pointer;
  display: flex;
  flex-flow: row;
  place-content: center;
  align-items: center;
  gap: 10px;
  height: 57px;
  padding: 0;
  position: relative;
  overflow: clip;
  text-decoration: none;
  background-color: var(--c-graphite);
  border-right: 1px solid var(--c-stone-65);
  border-bottom: 1px solid var(--c-stone-65);
}
.btn-dark__edge {
  z-index: 1;
  flex: none;
  width: 1px;
  position: absolute;
  top: 0; bottom: 0; left: -1px;
  background-color: var(--c-orange);
  transition: width .25s var(--ease-spring0), left .25s var(--ease-spring0);
}
.btn-dark:hover .btn-dark__edge,
.btn-dark:focus-visible .btn-dark__edge { width: 100%; left: 0; }
.btn-dark__all {
  display: flex;
  flex: 1 0 0;
  place-content: center;
  align-items: center;
  height: 100%;
  position: relative;
}
.btn-dark__text {
  display: flex;
  align-items: center;
  gap: 10px;
  position: relative;
}
.btn-dark__text .t-mono { color: var(--c-white); white-space: pre; user-select: none; }
.btn-dark__icon { width: 12px; height: 12px; position: absolute; top: 3px; left: -26px; }
.btn-dark__icon svg { width: 100%; height: 100%; }
.btn-dark:hover .btn-dark__text,
.btn-dark:focus-visible .btn-dark__text { z-index: 1; }
.btn-dark:hover .btn-dark__icon,
.btn-dark:focus-visible .btn-dark__icon { position: relative; top: auto; left: auto; }
@media (prefers-reduced-motion: reduce) {
  .btn-dark__edge { transition: none !important; }
}

/* ----------------------------------------------------------------
   INNER-ANNUAL, page-scoped override (content plan §3.11 / §4.6).
   Public price tiers are gone; the annual/monthly toggle goes with them.
   The control is hidden rather than deleted so js/main.js §5's price-toggle
   handler stays inert — the group keeps its default `annual` state and the
   annual price group is now the only one in the DOM.
   ---------------------------------------------------------------- */
.annual__nav { display: none; }

/* Engagement durations are much longer words than the template's 4-character
   prices: "4–8 weeks" at the 48px preset measures ~237px, which wrapped inside
   the 228px desktop cell and butted straight into the next column's "monthly"
   at 810. 40px + a 16px right inset keeps all three on one line with air on both
   sides at every breakpoint; the numbers column is bottom-aligned, so nothing
   else in the band moves. */
.annual__price-amount p { font-size: 40px; }
.annual__price-amount { padding-right: 16px; }


/* ================================================================
   HER  —  data-section="her"
   Heights: 651 @1440 / 617 @1280 / 779 @810 / 659 @390
   Root math: top-band(max(vh,min)) + 1px divider + foot-band(max/auto)
   ================================================================ */
.her {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  background-color: var(--c-cream);
  overflow: hidden;
}
/* The hero texture is NOT a cover-cropped background. In the reference
   (research/raw/<bp>/solutions.html, identical markup at all four widths) it is
   a horizontal marquee:

     .framer-1mq2g5k-container { position:absolute; inset:-216px -759px -59px -758px;
                                 opacity:.9; z-index:0 }
       > div[role=img] { height:100%; width:100%; overflow:hidden;
                         transform: scale(~1.05) }
           > div { position:absolute; top:0; left:0; height:100%; display:flex;
                   transform: translateX(-Npx) }
               > img{n} { height:100%; width:auto }   <- 2880x1374 source

   Two consequences the earlier `background-size:cover` model got wrong:
     1. The image is scaled to the box HEIGHT and tiled on x — it is never
        cropped vertically, so the full 1374px source always shows. `cover`
        instead width-fits and crops vertically, which is why those boxes had
        "~zero vertical slack" and background-position-y did nothing.
     2. The box is not the top band: it overhangs it by 216px above and 59px
        below (and 758/759px each side), so the source is mapped onto a box
        275px TALLER than .her__top, then scaled ~1.05 about its centre.

   Containing block is "Main header" == .her__top (position:relative), which is
   why the texture div is a child of .her__top in the markup.

   Validation (zero free parameters — inset + aspect + scale all read from the
   DOM, nothing calibrated). With H = .her__top height + 275, c = H/2, and the
   dune's leading edge at fraction f of the source:
       y_page(f) = -216 + c + (f*H - c) * scale
   Solving f from the measured desktop dune top (y307) gives f = 0.7096; the
   same f then predicts laptop y282.8 against a measured y283 — an independent
   breakpoint with a different box height (691 vs 725) and a different page
   offset, hit to within a pixel. */
.her__texture {
  position: absolute;
  inset: -216px -759px -59px -758px;
  z-index: 0;
  opacity: .9;
  background-image: url("/assets/images/iEh0WlD2mo4i3VmbpP52rJl0.png");
  background-size: auto 100%;
  background-repeat: repeat-x;
  /* x-phase == the captured translateX of the marquee row (arbitrary, it
     animates; these are the values in each breakpoint's DOM dump). */
  background-position: -60px top;
  transform: scale(1.05638);
  pointer-events: none;
}
.her__top {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  width: 100%;
  height: 50vh;
  min-height: 400px;
  padding: 0 var(--page-pad-desktop);
}
.her__top-inner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 18px;
  width: 100%;
  max-width: var(--page-max);
  height: 100%;
  margin: 0 auto;
}
.badge-pill.her__badge { margin-bottom: 4px; }
.her__heading {
  position: absolute;
  left: -1px;
  bottom: -8px;
  width: 100%;
  max-width: 540px;
}
.her__divider {
  position: relative;
  z-index: 1;
  flex: none;
  width: 100%;
  height: 1px;
  background-color: var(--c-stone);
}
.her__foot {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  width: 100%;
  height: 20vh;
  min-height: 200px;
  padding: 0 var(--page-pad-desktop);
  background-color: var(--c-cream);
}
.her__foot-inner {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
}
.her__buttons {
  display: flex;
  align-items: center;
  gap: 24px;
  width: 100%;
}
.her__years { display: flex; align-items: baseline; gap: 4px; flex: none; }
.her__years-num {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 48px;
  line-height: 100%;
  letter-spacing: -.05em;
  color: var(--c-graphite);
}
.her__years-plus {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 32px;
  color: var(--c-graphite);
}
.her__years-caption { color: var(--c-black); max-width: 120px; margin-left: 6px; }
.her__cta { flex: 1 0 0; max-width: 340px; }
/* Reference (research/raw/<bp>/solutions.html, every breakpoint) renders this
   button's icon fully absent at rest — no svg paints in any reference
   screenshot, only the centered "how it works" label. The shared .btn-dark
   component (css/sections/help.css) positions the icon at left:-26px relative
   to the (centered) text, which — unlike narrower CTAs elsewhere — stays
   inside this wide button's clipped box instead of falling outside it, so it
   was visible at rest here. Hidden via opacity so the existing hover/focus
   reveal mechanic still works. */
.her__cta .btn-dark__icon { opacity: 0; }
.her__cta:hover .btn-dark__icon,
.her__cta:focus-visible .btn-dark__icon { opacity: 1; }
.her__desc { max-width: 300px; color: var(--c-black); }

/* The reference indents the whole hero to the second grid column: every
   element (badge, heading, desc, stat row) inks at x491 against a 1376px
   content box, i.e. exactly 1376/3 = 458.67 in. The hand-rolled layout here
   anchored them all at the left margin (x32) instead. The vertical grid
   rules themselves were already correct, which is why only the content
   looked off. Stat row is the same 1:1:2 rhythm as /projects:
   num x491, caption x721, cta x950->1407. */
@media (min-width: 1440px) {
  .her__top-inner,
  .her__foot-inner { padding-left: 33.3333%; }
  /* abs-positioned: its containing block is the padding box, so padding-left
     does not move it -- it needs the offset applied directly. */
  .her__heading { left: calc(33.3333% - 1px); }
  /* gap:24 would make the halves (917-24)/2=446.5 and push the CTA to x961;
     the reference has it at x950 == a clean half of 917. */
  .her__buttons { gap: 0; }
  .her__years { flex: 1 0 0; }
  .her__years-caption {
    margin-left: auto;
    width: 50%;
    max-width: none;
    flex: none;
  }
  .her__cta { flex: 1 0 0; max-width: none; }
  .her__desc { max-width: 320px; }
  /* The "+" is the same 48px as the digit, not 32px: reference ink measures
     21x22 against the clone's 14x14 (the digit itself is 23x34 in both). */
  .her__years-plus { font-size: 48px; }
  /* Foot band is not a centred (or simply bottom-aligned) stack: the
     reference pins the desc 36px below the band top (band 451 -> desc box
     487) and the buttons row flush to the band bottom (651), exactly as
     /projects does with .her__blurb padding-top:36 + .her__buttons bottom:0.
     Centring floated the block 37px high; plain bottom-alignment then
     overshot the desc the other way. */
  .her__foot { align-items: stretch; }
  .her__foot-inner { height: 100%; padding-top: 36px; gap: 0; }
  .her__buttons { margin-top: auto; }
  /* Baseline alignment stacked num(48) + plus(59) + 2-line caption(41) into
     a 73px row; the reference row is the CTA's 57px with the digit and
     caption centred against each other (ink centres y620 vs y619). */
  .her__years { align-items: center; }
  .her__years-plus { line-height: 48px; }
}

@media (min-width: 810px) and (max-width: 1439.98px) {
  .her__foot { height: 22vh; }
  /* Desc and buttons are BOTH absolutely positioned here, and they overlap.
     At these two widths the desc runs to three lines and the reference simply
     draws the "5+" on top of its last line -- .framer-1jb2dlv is
     {position:absolute; top:0; padding:36px 0 0} and .framer-1o7yuge is
     {position:absolute; bottom:0}, so neither block reserves flow height.
     Stacking them instead (padding-top + margin-top:auto) made the column
     36+60+127 = 223px of content in a 200px band: the CTA overflowed past the
     band bottom and .her's overflow:hidden clipped it to 15px, while the
     default flex-shrink on the buttons squashed it further.
     Reference ink, both reproduced exactly by this rule:
       laptop  band 417-617: desc 457/477/497, "5+" 494-528, CTA 560-616
       tablet  band 541-779: desc 581/601/622, "5+" 656-690, CTA 722-778 */
  .her__foot { align-items: stretch; }
  .her__foot-inner { position: relative; height: 100%; gap: 0; }
  .her__desc { position: absolute; top: 36px; left: 0; }
  .her__buttons { position: absolute; bottom: 0; left: 0; width: 100%; }
  /* 320, not 420: reference desc ink stops at x345 (313 wide) at both
     laptop and tablet, same box as desktop. */
  .her__desc { max-width: 320px; }
  /* Same shape as /projects at these widths: num + caption share row 1 with
     the caption on the 1/3 column, and the CTA spans the full content box on
     row 2 (ref ink 33->1247 laptop, 33->777 tablet; the 340px cap stopped it
     at 578). margin-left:auto against a 2/3 basis puts the caption at
     32+1216-810.7=437 (laptop) and 32+746-497.3=280.7 (tablet). */
  .her__buttons { flex-wrap: wrap; gap: 22px 0; }
  /* Centred, not baseline-aligned -- same as desktop. Baseline alignment lets
     the caption's second line grow the row below the shared baseline, which
     lifted the 48px digit 17px above the reference (tablet ink 639-673 vs
     656-690) while pushing the caption 5-6px the other way. */
  .her__years { flex: 0 0 100%; align-items: center; }
  .her__years-caption { margin-left: auto; flex: 0 0 66.6667%; max-width: none; }
  .her__cta { flex: 0 0 100%; max-width: none; }
}
/* Per-breakpoint marquee state, straight from each DOM dump's inline
   transforms. The scale is the wrapper's; the x offset is the marquee row's
   translateX at capture time. Box height (and therefore the dune's y) follows
   automatically from .her__top's height -- nothing here is calibrated. */
@media (min-width: 1280px) and (max-width: 1439.98px) {
  .her__texture { transform: scale(1.05614); background-position-x: -96px; }
}
@media (min-width: 810px) and (max-width: 1279.98px) {
  .her__texture { transform: scale(1.0569); background-position-x: -57px; }
}

@media (max-width: 809.98px) {
  .her__texture { transform: scale(1.04905); background-position-x: -63px; }
  .her__top { height: 40vh; min-height: 300px; padding: 0 var(--page-pad-phone); }
  .her__heading { width: 90%; max-width: 440px; bottom: -6px; left: 0; }
  /* Foot band uses the SAME mechanic as desktop, not a content-sized stack:
     the reference pins the desc to the band top with padding-top:36px
     (.framer-1jb2dlv { position:absolute; top:0; padding:36px 0 0 }) and the
     buttons row flush to the band bottom (.framer-1o7yuge { bottom:0 }).
     Measured against reference/phone/solutions.jpeg: divider y338, desc ink
     lines y379/399/419, "5+" y488-522, caption y541-551 + y561-571, CTA
     y602-658, hero ends y659 -- i.e. desc box top = 339+36 = 375 (ink 379)
     and the 57px CTA flush at the bottom. Height is fixed at 320px so the
     section still measures 338+1+320 = 659 (reference-heights.json). */
  .her__foot { height: 320px; min-height: unset; padding: 0 var(--page-pad-phone); align-items: stretch; }
  .her__foot-inner { height: 100%; padding-top: 36px; gap: 0; }
  .her__buttons { flex-direction: column; align-items: flex-start; gap: 20px; margin-top: auto; }
  /* The num and the caption DO stack at phone -- but the "5" and the "+" stay
     on one line. In the reference the outer "Num container" (.framer-8woy49)
     goes flex-direction:column; gap:10px, while the INNER "Num container"
     (.framer-tej8xq) that holds the digit and the plus stays flex-flow:row.
     The previous reading collapsed both, which split "5" and "+" onto separate
     lines. Reproduced here with wrap + a full-basis caption, so the digit and
     plus (which are siblings in this markup) keep sharing row 1. */
  .her__years { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 10px 0; }
  .her__years-caption { flex: 0 0 100%; margin-left: 0; max-width: 180px; }
  .her__cta { flex: none; width: 100%; max-width: none; }
  /* .framer-7ciz9n is width:338px at phone == the full 390-2*26 content box. */
  .her__desc { max-width: none; }
}

/* ================================================================
   INDUSTRIES  —  data-section="industries"
   Heights: 900 @1440 (82vh/min900) / 800 @1280 / 973 @810 / 802 @390
   (tablet+phone: height:min-content — content driven, tuned via QA)
   ================================================================ */
.industries {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 82vh;
  min-height: 900px;
  background-color: var(--c-cream);
  overflow: hidden;
}
.industries__header {
  display: flex;
  align-items: flex-end;
  width: 100%;
  height: 20vh;
  min-height: 200px;
  padding: 0 var(--page-pad-desktop);
}
.industries__header-inner {
  display: flex;
  align-items: flex-end;
  gap: 18px;
  width: 100%;
  max-width: var(--page-max);
  height: 100%;
  padding-bottom: 42px;
  margin: 0 auto;
}
.industries__body {
  display: flex;
  align-items: stretch;
  width: 100%;
  height: 60%;
  min-height: 600px;
  padding: 0 var(--page-pad-desktop);
}
.industries__body-inner {
  display: flex;
  align-items: stretch;
  gap: 32px;
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
}
.industries__message {
  flex: 1 0 0;
  max-width: 230px;
  padding-top: 72px;
  color: var(--c-black);
}
.industries__grid {
  flex: 2 0 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
}
.industries__cell {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px 24px 26px 20px;
  border-top: 1px solid var(--c-stone-85);
}
.industries__grid > .industries__cell:nth-child(1),
.industries__grid > .industries__cell:nth-child(2) { border-top: none; }
.industries__grid > .industries__cell:nth-child(odd) { border-right: 1px dashed var(--c-stone-85); }
.industries__icon-frame {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 57px;
  height: 57px;
  background-color: var(--c-white);
  flex: none;
}
.industries__icon-frame svg { width: 70%; height: 70%; }
.industries__title { color: var(--c-graphite); margin-top: 4px; }
.industries__list { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.industries__list li { display: flex; align-items: baseline; gap: 8px; }
.industries__list .chev { color: var(--c-orange); font-family: var(--font-mono); font-size: 15px; }
.industries__list span.t-body-sm { color: var(--c-body); }

/* Industries follows the same second-column indent as the hero, and each
   cell is a 2x2 block rather than a single stack:
     [icon      ][ list ]
     [title(bot) ][ list ]
   Stacking icon/title/list in one flex column made every row ~275px tall
   instead of the reference's 200 (600px body / 3 rows), which is what
   smeared the diff across the whole 800-2200 range. Column math: with
   .industries__body-inner gap:0, message flex:1 -> 32..490.67 and grid
   flex:2 -> 490.67..1407.3, matching the reference's 491 / 1407. */
@media (min-width: 1440px) {
  .industries__header-inner {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;
    gap: 10px;
    padding-left: 33.3333%;
  }
  .industries__body-inner { gap: 0; }
  /* margin-left:auto, not flex:1 on the message -- the message's own
     max-width:230px caps that flex item at 230, so the grid started at x284
     instead of x491. Pinning the grid to 2/3 and pushing it right puts its
     edges at 490.67 / 1407.3, matching the reference rules at 491 / 1407. */
  .industries__grid {
    flex: 0 0 66.6667%;
    margin-left: auto;
    grid-template-rows: repeat(3, 1fr);
  }
  .industries__cell {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto 1fr;
    gap: 0;
    /* Icon sits flush on the sub-column rule AND on the row top: the
       reference frame is a clean 57px white run at y852-908 against a grid
       row starting at y851, so there is essentially no top inset. */
    padding: 1px 0 15px 0;
  }
  .industries__icon-frame { grid-column: 1; grid-row: 1; align-self: start; }
  /* Title and list ink both bottom out at y1019 in the reference. They share
     a content-box bottom, but the mono title's ink sits 6px above it against
     the list's 18px, so the title needs 12px of lift to line up. */
  .industries__title { grid-column: 1; grid-row: 2; align-self: end; margin-top: 0; margin-bottom: 12px; }
  .industries__list {
    grid-column: 2; grid-row: 1 / -1; align-self: end; margin-top: 0;
    /* the mid-cell rules at x720 / x1179 */
    border-left: 1px dashed var(--c-stone-85);
    padding-left: 20px;
    height: 100%;
    justify-content: flex-end;
  }
  .industries__grid > .industries__cell:nth-child(odd) { border-left: 1px solid var(--c-stone-85); }
  .industries__grid > .industries__cell:nth-child(even) { border-right: 1px solid var(--c-stone-85); }
}
.industries__footer {
  display: flex;
  align-items: center;
  width: 100%;
  height: 10vh;
  min-height: 100px;
  padding: 0 var(--page-pad-desktop);
}

@media (min-width: 810px) and (max-width: 1439.98px) {
  .industries { height: min-content; min-height: unset; }
  .industries__body { height: 60vh; min-height: unset; }
  .industries__message { max-width: 200px; padding-bottom: 25px; }
  /* The second-column indent and the stacked badge-over-heading are NOT
     desktop-only. "Header part" (.framer-1vmgjbt) is flex:2 0 0 against a
     flex:1 spacer with no override above phone, and "Header container"
     (.framer-1opif00) is flex-flow:column at every width -- so laptop and
     tablet get the same treatment. The clone had the badge inline at the left
     margin instead: reference ink starts at x437 on laptop (32 + 1216/3 =
     437.3), the clone put it at x33. */
  .industries__header-inner {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;
    padding-left: 33.3333%;
  }
}
@media (max-width: 809.98px) {
  .industries { height: min-content; min-height: unset; }
  .industries__header { height: min-content; min-height: 140px; padding: 0 var(--page-pad-phone); align-items: center; }
  .industries__header-inner { align-items: center; padding-bottom: 0; }
  .industries__header .badge-pill { display: none; }
  /* Reference wraps "Industries we automate" to two lines at phone width
     (research/raw/phone/solutions.html + reference/phone/solutions.jpeg);
     unconstrained it fits on one line here since t-h2 has no page max-width
     of its own. min-height:140px absorbs the extra line either way, so this
     doesn't move the section's measured height. */
  .industries__heading { max-width: 260px; }
  .industries__body { height: min-content; min-height: 200px; padding: 0 var(--page-pad-phone); }
  .industries__message { display: none; }
  .industries__grid { grid-template-columns: 1fr 1fr; }
  .industries__cell { padding: 24px 12px 20px 0; min-height: 200px; }
  .industries__footer { height: 60px; min-height: unset; padding: 0 var(--page-pad-phone); }
}

/* ================================================================
   STACK  —  data-section="allstack"
   Moved off the homepage (content plan §3.8 / §4.5). The section root,
   header and footer bands mirror `industries` so the band rhythm on this
   page is unchanged: header 20vh(min200) + 1px hairline + body(flex) +
   footer 20vh(min200) inside an 82vh / min-900 root.

   The radial "Scheme" geometry below is the ORIGINAL clone's, copied from
   oberon-clone/css/pages/home.css (.hm-stack__*) with the `hm-` prefix
   dropped. Everything there is the Framer component's own geometry on a
   1376x392 stage; the one adaptation is the stage anchor — home.css insets
   it by --page-pad-desktop against a padded .hm-cc, whereas here the
   padding already lives on .stack__body, so the stage spans its inner box
   (left:0/right:0). At 1440 both resolve to the same 1376px box.
   ================================================================ */
.stack {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 82vh;
  min-height: 900px;
  background-color: var(--c-white);
  overflow: hidden;
}
.stack__header {
  display: flex;
  align-items: flex-end;
  width: 100%;
  height: 20vh;
  min-height: 200px;
  padding: 0 var(--page-pad-desktop);
}
.stack__header-inner {
  display: flex;
  align-items: flex-end;
  gap: 18px;
  width: 100%;
  max-width: var(--page-max);
  height: 100%;
  padding-bottom: 42px;
  margin: 0 auto;
}
.stack__body {
  position: relative;
  display: flex;
  /* stretch, NOT center: below 1440 the section root is height:min-content, so
     the body's height comes from its own min-height and a percentage height on
     the child cannot resolve. `height:100%` there computed to 0 and the inner
     box (overflow:clip) clipped the whole scheme away — the band rendered
     blank at 810. align-self:stretch resolves at every tier. */
  align-items: stretch;
  flex: 1 0 auto;
  width: 100%;
  min-height: 450px;
  padding: 0 var(--page-pad-desktop);
  overflow: var(--overflow-clip-fallback, clip);
}
.stack__body-inner {
  position: relative;
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
  overflow: var(--overflow-clip-fallback, clip);
}
.stack__footer {
  display: flex;
  align-items: center;
  width: 100%;
  height: 20vh;
  min-height: 200px;
  padding: 0 var(--page-pad-desktop);
}
.stack__footer-inner {
  display: flex;
  align-items: center;
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
}
/* same 300px balanced-wrap box the reference uses for this caption */
.stack__msg { max-width: 300px; text-wrap: balance; color: var(--c-black); }

/* Same second-column indent as industries / processes (§ the 1/3 rail). */
@media (min-width: 1440px) {
  .stack__header-inner {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;
    gap: 10px;
    padding-left: 33.3333%;
  }
  .stack__footer-inner { padding-left: 33.3333%; }
}
@media (min-width: 810px) and (max-width: 1439.98px) {
  .stack { height: min-content; min-height: unset; }
  .stack__body { min-height: 499px; }
  .stack__header-inner {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;
    padding-left: 33.3333%;
  }
  .stack__footer-inner { padding-left: 33.3333%; }
}
@media (max-width: 809.98px) {
  .stack { height: min-content; min-height: unset; }
  .stack__header { height: min-content; min-height: 140px; padding: 0 var(--page-pad-phone); align-items: center; }
  .stack__header-inner { align-items: center; padding-bottom: 0; }
  .stack__header .badge-pill { display: none; }
  .stack__heading { max-width: 260px; }
  .stack__body { min-height: 400px; padding: 0 var(--page-pad-phone); }
  .stack__footer { height: auto; min-height: 120px; padding: 0 var(--page-pad-phone); }
}

/* --- the radial "Scheme" (verbatim geometry, see note above) --- */
.stack__scheme {
  position: absolute; top: 49%;
  left: 0; right: 0;
  transform: translateY(-50%);
  height: 392px;
}
/* The eight tiles are placed symmetrically about the stage centre at any stage
   width (`--ul right:847 / --ur left:847` mirror each other for every W), but the
   decorations inside .stack__core — dot fields, square, angle marks — carry raw
   px offsets that only land under the logo when the stage is exactly the 1376px
   the component was designed at. Anchoring .stack__core as a fixed 1376px box
   centred on the stage keeps that cluster under the logo at every width; without
   it the square + dots detached to the right of the mark at 810 and read as a
   broken band. (home.css leaves this as inset:0 because the homepage had a
   reference screenshot to match; this section has none.) */
.stack__core {
  position: absolute;
  top: 0; bottom: 0;
  left: 50%;
  width: 1376px;
  transform: translateX(-50%);
  z-index: 1;
}
.stack__dots, .stack__sq, .stack__ang { position: absolute; }
.stack__dots--orange { top: 67px; left: 560px; width: 258px; height: 284px; }
.stack__dots--dark   { top: 22px; left: 486px; width: 400px; height: 355px; }
.stack__dots--grey   { top: 10px; left: 526px; width: 325px; height: 357px; }
.stack__sq  { top: 165px; left: 641px; width: 95px; height: 95px; }
.stack__ang { top: 177px; left: 653px; width: 71px; height: 71px; }
.stack__logo {
  position: absolute; top: 54%; left: 50%;
  transform: translate(-50%, -50%);
  width: 38px; height: 38px; display: block;
}
.stack__logo img { display: block; width: 38px; height: 38px; }

/* the seven dashed spokes (z-index:0, 1px dashed #9e9e9e) */
.stack__spoke {
  position: absolute; z-index: 0; height: 0;
  border-top: 1px dashed rgb(158, 158, 158);
}
.stack__spoke--h   { top: calc(53.5714% - .5px); left: 31.5%; width: 37%; opacity: .6; }
.stack__spoke--ht  { top: calc(53.5714% - .5px); left: 38%;   width: 24%; opacity: .2; transform: rotate(-15deg); }
.stack__spoke--d1  { top: calc(53.5714% - .5px); left: 36.5%; width: 27%; opacity: .6; transform: rotate(-30deg); }
.stack__spoke--d1t { top: calc(53.5714% - .5px); left: 44%;   width: 12%; opacity: .2; transform: rotate(-60deg); }
.stack__spoke--d2  { top: calc(53.5714% - .5px); left: 36.5%; width: 27%; opacity: .6; transform: rotate(30deg); }
.stack__spoke--v, .stack__spoke--vt {
  height: auto; width: 0; border-top: 0;
  border-left: 1px dashed rgb(158, 158, 158);
  left: calc(50% - .5px);
}
.stack__spoke--v  { top: 15.5714%; height: 76%; opacity: .6; }
.stack__spoke--vt { top: 20.0714%; height: 67%; opacity: .2; transform: rotate(-30deg); }

/* a 70x70 pill clipping a 54x24 contained, desaturated logo */
.stack__tile {
  position: absolute; z-index: 3;
  width: 70px; height: 70px;
  border-radius: 240px; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.stack__tile img {
  width: 54px; height: 24px; object-fit: contain; object-position: center;
  filter: brightness(0.83) grayscale(1);
}
.stack__tile--top   { left: 50%; transform: translateX(-50%); bottom: 322px; }
.stack__tile--bot   { left: 50%; transform: translateX(-50%); top: 347px; }
.stack__tile--ul    { right: 847px; bottom: 253px; }
.stack__tile--ur    { left: 847px;  bottom: 253px; }
.stack__tile--left  { right: 956px; top: 54%; transform: translateY(-50%); }
.stack__tile--right { left: 958px;  top: 54%; transform: translateY(-50%); }
.stack__tile--ll    { right: 847px; top: 280px; }
.stack__tile--lr    { left: 849px;  top: 280px; }

/* phone runs the reference's 338px stage variant, with .stack__core widened
   to 107% and its own tile offsets. */
@media (max-width: 809.98px) {
  .stack__core { left: -3.5%; width: 107%; right: auto; transform: none; }
  .stack__dots--orange { left: 52px; }
  .stack__dots--dark   { left: -22px; }
  .stack__dots--grey   { left: 18px; }
  .stack__sq  { top: 170px; left: 141px; width: 81px; height: 81px; }
  .stack__ang { top: 180px; left: 151px; width: 61px; height: 61px; }
  .stack__spoke--h, .stack__spoke--ht { width: 60%; left: 20%; }
  .stack__spoke--d1, .stack__spoke--d1t, .stack__spoke--d2 { width: 57%; left: 21.5%; }
  .stack__spoke--v, .stack__spoke--vt { top: 33.5714%; height: 40%; }
  .stack__tile--right { left: 274px; }
  .stack__tile--left  { right: 277px; }
  .stack__tile--lr    { top: 232px; left: 237px; }
  .stack__tile--ll    { top: 232px; right: 242px; }
  .stack__tile--ur    { bottom: 210px; left: 236px; }
  .stack__tile--ul    { bottom: 212px; right: 241px; }
  .stack__tile--bot   { top: 277px; }
  .stack__tile--top   { bottom: 252px; }
}

/* ================================================================
   PROCESSES  —  data-section="processes"
   Heights: 800 @1440 (80vh/min800) / 801 @1280 / 865 @810 / 755 @390
   ================================================================ */
.processes {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 80vh;
  min-height: 800px;
  background-color: var(--c-white);
  overflow: hidden;
}
.processes__header {
  display: flex;
  align-items: flex-end;
  width: 100%;
  height: 20vh;
  min-height: 200px;
  padding: 0 var(--page-pad-desktop);
}
.processes__header-inner {
  display: flex;
  align-items: flex-end;
  gap: 18px;
  width: 100%;
  max-width: var(--page-max);
  height: 100%;
  padding-bottom: 42px;
  margin: 0 auto;
}
.processes__body {
  display: flex;
  align-items: stretch;
  width: 100%;
  height: 40vh;
  min-height: 400px;
  padding: 0 var(--page-pad-desktop);
}
.processes__body-inner {
  display: flex;
  align-items: stretch;
  gap: 32px;
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
}
.processes__message {
  flex: 1 0 0;
  max-width: 230px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-top: 72px;
}
.processes__ava-row { display: flex; align-items: center; gap: 10px; }
.processes__ava { width: 32px; height: 32px; flex: none; overflow: hidden; }
.processes__ava img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* [CONFIRM] founder photo. Until a real portrait exists the avatar slot holds
   the Autnex mark on a stone tile rather than the template's stock portrait —
   same 32px box, so nothing in the band moves. */
.processes__ava--mark {
  display: flex; align-items: center; justify-content: center;
  background-color: var(--c-stone-light);
}
.processes__ava--mark img { width: 18px; height: 18px; object-fit: contain; }
.processes__name { color: var(--c-grey); }
.processes__quote { color: var(--c-black); max-width: 230px; }
.processes__grid {
  flex: 2 0 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-content: space-between;
}
.processes__cell {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 32px 24px 26px 20px;
  border-top: 1px solid var(--c-stone-cool-50);
}
.processes__grid > .processes__cell:nth-child(1),
.processes__grid > .processes__cell:nth-child(2) { border-top: none; padding-top: 0; }
.processes__grid > .processes__cell:nth-child(odd) { border-right: 1px solid var(--c-stone-cool-50); }
.processes__icon { width: 51px; height: 51px; flex: none; }
.processes__icon svg { width: 100%; height: 100%; }
/* "How we build" is a sequence, so the four abstract template icons are
   replaced by the step numbers the content plan gives (§4.4). Same 51px box,
   so the cell's 2x2 grid geometry is untouched. */
.processes__icon--num { display: flex; align-items: center; justify-content: flex-start; }
.processes__num { color: var(--c-orange); }
.processes__title { color: var(--c-graphite); max-width: 170px; }
.processes__desc { color: var(--c-body); max-width: 180px; }

/* Same second-column indent and 2x2 cell as industries. Processes differs in
   one respect: the reference draws only two vertical rules here (x949 cell
   split, x1407 grid edge) -- no mid-cell divider and no left rule -- so the
   desc column sits flush at x722 with no inset. Row top y1751, title and
   desc ink both bottom out at ~y1920. */
@media (min-width: 1440px) {
  .processes__header-inner {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;
    gap: 10px;
    padding-left: 33.3333%;
  }
  .processes__body-inner { gap: 0; }
  .processes__grid {
    flex: 0 0 66.6667%;
    margin-left: auto;
    grid-template-rows: repeat(2, 1fr);
    align-content: stretch;
  }
  .processes__cell {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto 1fr;
    gap: 0;
    padding: 1px 0 15px 0;
  }
  .processes__grid > .processes__cell:nth-child(1),
  .processes__grid > .processes__cell:nth-child(2) { padding-top: 1px; }
  .processes__icon { grid-column: 1; grid-row: 1; align-self: start; }
  .processes__title { grid-column: 1; grid-row: 2; align-self: end; margin-bottom: 12px; }
  .processes__desc { grid-column: 2; grid-row: 1 / -1; align-self: end; margin-bottom: 12px; }
}
.processes__footer {
  display: flex;
  align-items: center;
  width: 100%;
  height: 20vh;
  min-height: 200px;
  padding: 0 var(--page-pad-desktop);
}
.processes__footer-inner {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
}
.processes__more { display: flex; align-items: center; gap: 8px; }
.processes__more .t-h3-caps { color: var(--c-orange); }

@media (min-width: 810px) and (max-width: 1439.98px) {
  .processes { height: min-content; }
  /* Same stacked + second-column-indented header as industries. */
  .processes__header-inner {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;
    padding-left: 33.3333%;
  }
}
@media (max-width: 809.98px) {
  .processes { height: min-content; min-height: 755px; }
  .processes__header { height: min-content; min-height: 140px; padding: 0 var(--page-pad-phone); align-items: center; }
  .processes__header .badge-pill { display: none; }
  .processes__header-inner { align-items: center; padding-bottom: 0; }
  /* Wraps to two lines in the reference, same as .industries__heading:
     ref ink 1503-1525 + 1540-1557 against the clone's single 1518-1540. */
  .processes__heading { max-width: 260px; }
  .processes__body { height: min-content; min-height: unset; padding: 0 var(--page-pad-phone); }
  /* The 42px sits on the message itself ("Message container" .framer-h1xhex is
     padding:0 0 42px at phone), not as a gap on the body -- with a 24px gap the
     grid started at y1702 against the reference's y1753. */
  .processes__body-inner { flex-direction: column; gap: 0; }
  .processes__message { max-width: none; padding-top: 0; padding-bottom: 42px; gap: 10px; }
  /* Avatar sits ABOVE the name at phone, not beside it: "Ana and name"
     (.framer-1il7fvk) is flex-flow:column; gap:8px, holding the Ava container
     and the Name as separate rows, and the quote follows at the End
     component's gap:10px. Chain measured off reference/phone/solutions.jpeg:
     avatar 1601-1632, name ink ~1650 (light grey, only just inks), quote
     1674-1684 + 1694-1704 -- i.e. 32 + 8 + 20 + 10. The clone had the pair
     inline, which pulled the quote 25px high. */
  .processes__ava-row { flex-direction: column; align-items: flex-start; gap: 8px; }
  .processes__grid { grid-template-columns: 1fr 1fr; }
  /* Cell is a space-between column in a 200px-capped row, not a snug stack:
     "Content container" (.framer-4uys3j) is
     {flex-flow:column; place-content:flex-start space-between; padding:32px 0 0}
     inside a row that is {height:min-content; max-height:200px}, and the title
     (.framer-3kagwu) carries padding:0 0 26px. That pushes the icon to the row
     top and the title to the row bottom with the slack in between, which is
     the ~51px icon-to-title gap the clone was missing (clone gap 40 -> ref 82).
     Checks out on reference/phone/solutions.jpeg: row 1 content top 1753 gives
     icon ink at 1753+32 = 1785 (measured 1785) and the title bottom-aligned at
     1891-1923; the clone had them at 1734 and 1799. */
  .processes__cell { padding: 32px 12px 26px 0; height: 200px; justify-content: space-between; gap: 0; }
  /* The base rule zeroes padding-top on the first two cells (correct at
     desktop, where row 1 sits flush under the header). At phone the reference
     pads every row equally, and that :nth-child rule outranks the class
     selector above on specificity, so it has to be undone explicitly --
     without this the row-1 icons rendered 32px high (ink 1752 vs ref 1785)
     while the bottom-anchored titles still lined up, which is what made the
     mismatch look like a cell-height problem rather than a padding one. */
  .processes__grid > .processes__cell:nth-child(1),
  .processes__grid > .processes__cell:nth-child(2) { padding-top: 32px; }
  .processes__footer { height: auto; max-height: 60px; min-height: unset; padding: 0 var(--page-pad-phone); }
}

/* ---------------------------------------------------------------
   DECORATIVE ICON STROKE DRAW  (industries grid + process steps)
   ---------------------------------------------------------------
   The 6 industry icons and 4 process icons are not still line art: the
   reference perpetually draws and un-draws their outlines. The clone shipped
   the same SVGs with one frame of that loop baked into stroke-dashoffset.

   MEASURED live 2026-08-18 (Playwright, /solutions, computed style sampled
   every rAF for 9s after scrolling the whole page so every icon had been in
   view):
     - 24 paths animate. They are exactly the paths whose stroke-dasharray is
       "L L" (dash == gap == the path length); the "2,5" / "1.71,3.42" dotted
       decorations and the dasharray:0 paths are static and were left alone.
     - all 24 share ONE clock — max spread across all of them was 6.5e-5 of a
       path length, and it does not reset when an icon scrolls out of view, so
       a plain page-load-anchored CSS loop reproduces it.
     - the shared value is a normalised progress p: dashoffset = -p * L on
       every path. p walks the integers, one per 1817ms: it DWELLS ~650ms at
       each integer, then eases ~1167ms to the next (an overdamped spring with
       a ~0.15% overshoot, transcribed below as measured stops rather than
       guessed at with a bezier).
     - with dash == gap == L, an even p paints the outline solid and an odd p
       hides it entirely, so one full visual cycle is TWO integer steps: draw
       on, hold, wipe off, hold => 3634ms.

   Implementation: the 24 paths carry pathLength="1", so dasharray "1 1" and
   dashoffset are in normalised units and one keyframe set drives paths of
   wildly different real lengths (5.5 to 183.4 user units). Pure CSS — no JS,
   nothing that can affect layout, and it degrades to a fully-drawn outline
   (dashoffset 0, the attribute baked in the markup) when animations are off. */
.icon-draw {
  animation: icon-draw 3634ms linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .icon-draw { animation: none; }
}
@keyframes icon-draw {
  0% { stroke-dashoffset: 0; }
  17.8866% { stroke-dashoffset: 0; }
  21.098% { stroke-dashoffset: -0.1024; }
  24.3093% { stroke-dashoffset: -0.3351; }
  27.5206% { stroke-dashoffset: -0.5588; }
  30.732% { stroke-dashoffset: -0.7305; }
  33.9433% { stroke-dashoffset: -0.8479; }
  37.1547% { stroke-dashoffset: -0.92; }
  40.366% { stroke-dashoffset: -0.9618; }
  43.5773% { stroke-dashoffset: -0.9843; }
  46.7887% { stroke-dashoffset: -0.9952; }
  50% { stroke-dashoffset: -1; }
  67.8866% { stroke-dashoffset: -1; }
  71.098% { stroke-dashoffset: -1.1024; }
  74.3093% { stroke-dashoffset: -1.3351; }
  77.5206% { stroke-dashoffset: -1.5588; }
  80.732% { stroke-dashoffset: -1.7305; }
  83.9433% { stroke-dashoffset: -1.8479; }
  87.1547% { stroke-dashoffset: -1.92; }
  90.366% { stroke-dashoffset: -1.9618; }
  93.5773% { stroke-dashoffset: -1.9843; }
  96.7887% { stroke-dashoffset: -1.9952; }
  100% { stroke-dashoffset: -2; }
}

/* ---------------------------------------------------------------
   WORD-REVEAL STAGGER OVERRIDE  (measured live 2026-08-19)
   ---------------------------------------------------------------
   The reference word-fades every mono caption and body line on this
   page (opacity only — armed spans read o=0.001, transform identity).
   The reveals themselves come from data-split-text="words" in the
   HTML + shared js/text-reveal.js; the shared 0.12s/word preset is
   right for the short captions (industries list items, card titles,
   quote — live ~120-150ms/word), but the live effect spreads long
   paragraphs across a roughly fixed ~0.87s window: the 15-word hero
   description measured ~46ms/word (first word in at ~305ms after the
   post-hydration intro start, last ending ~1.1s). Override the var
   here — text-reveal.css defines it on [data-split-text], this is the
   supported hook. Opacity-only, so height-invariant; no reduced-motion
   guard needed (text-reveal.js never arms under reduced motion). */
.her__desc[data-split-text] { --sr-stagger: .048s; } /* 15 words / ~700ms spread */
