/* ── FONT-SWAP LAYOUT SHIFT (chunk 11D3) ─────────────────────────────────────
   The page loads Google Sans Flex with font-display:swap, so first paint is set
   in whatever system face the stack falls through to and the real face replaces
   it a few hundred milliseconds later. The two have different metrics, so
   everything reflows on the swap. Measured at 1440x900 with the real binaries
   served: CLS 0.0248, a single shift, sourced to UL.nav-links and DIV.nav-cta —
   the nav row, whose items are laid out by their own text width. The shift's
   timing tracks the font delay exactly (334ms at 0ms delay, 1071ms at 800ms),
   which is what identifies it as the swap and not the reveal observer.

   The fix is a fallback face declared with metrics overridden to match Google
   Sans Flex, inserted into the stack ahead of the raw system fonts. It has no
   src of its own beyond local() references, so it downloads nothing: it is the
   system font the page would have used anyway, re-declared at the webfont's
   proportions. The swap then exchanges two faces that already set to the same
   width, and there is nothing left to reflow.

   Google's own @font-face blocks arrive from the fonts.googleapis.com
   stylesheet and cannot be edited, which is why this is a separate family in the
   stack rather than a size-adjust on the real face.

   MEASURED INPUTS. Rendered at 1000px in this sandbox, average advance over a
   118-character sample of the nav labels plus both alphabets, and the font
   bounding box:
     Google Sans Flex          avg 506.606   ascent 966   descent 286
     Arial / Helvetica /
       Liberation Sans         avg 511.990
     Segoe UI / Roboto         avg 480.973
   size-adjust  = GSF avg / fallback avg
   ascent-override  = (GSF ascent / 1000) / size-adjust      (likewise descent)

   THE SHIPPED size-adjust IS 102.19%, NOT THE 98.95% THAT FORMULA GIVES, and the
   Segoe/Roboto face carries the same +3.28% correction (105.33 -> 108.78). The
   average-advance formula weights every glyph equally at weight 400; the only
   element that actually shifts is the nav, which sets eight specific strings at
   weight 500, and Google Sans Flex's 500 is wider against a synthesised 500 than
   its 400 is against a real 400. Summed at 1000px over the nav's own strings:
     Google Sans Flex   34,589.2
     fallback @ 98.95%  33,493.7   (-3.17%)
   102.19% is the value that makes those two sums equal. Swept against measured
   CLS with a 500ms font delay, 1440x900 and 380x740:
      98.95%  CLS 0.0088 / 0.0004      101.50%  CLS 0.0002 / 0.0004
     100.50%  CLS 0.0002 / 0.0004      102.19%  CLS 0.0001 / 0.0004
     103.00%  CLS 0.0001 / 0.0004
   Tuning to the nav rather than to the average is safe here because the nav is
   the ONLY source the layout-shift observer ever attributes a shift to — every
   other block on the page is in a container whose width does not depend on its
   own text, so a few percent of advance difference moves nothing that counts.

   TWO FACES, NOT ONE, because the two fallback groups need different numbers and
   a single family name with two src lists is decided by cascade order rather than
   by which local() actually resolved. As separate families the stack does the
   choosing: on Windows and Android the Segoe/Roboto face resolves first and wins;
   on macOS, iOS and Linux it finds nothing and falls through to the Helvetica /
   Arial face. Neither resolves on a system with only DejaVu, which then lands on
   system-ui exactly as before — the override can only help, never hurt.

   VERIFIED for the Helvetica/Arial face, which resolves in this sandbox through
   Liberation Sans. The Segoe/Roboto face cannot be verified here because neither
   font is installed; its numbers come from the same measurement method against
   the metrics Chromium reports for those families, and it is flagged rather than
   claimed. */
/* size-adjust was 102.19%, calibrated on vertical metrics alone. That matched the
   line box but not the advance width: measured at 768x1024 with the real face
   loaded, the hero headline sets 1616.1px in Google Sans Flex against 1497.9px in
   this fallback, 7.9% narrower. The headline therefore wrapped one line shorter
   in the fallback and gained that line back on swap, growing .hero .container
   from 440px to 480px and moving everything centred inside it. That single reflow
   was the entire remaining cumulative layout shift on the page: 0.00922 at
   768x1024, against a 0.005 target.
   107% closes the width gap. ascent-override and descent-override are rescaled by
   102.19/107 so size-adjust x override is unchanged and the line box stays exactly
   where the original calibration put it — this widens the fallback's glyphs
   without moving a single baseline.
   Measured CLS over a full scroll of the page, same harness, three widths:
     102.19%   1440 0.00013   768 0.00922   380 0
     105%      1440 0         768 0.00922   380 0
     107%      1440 0         768 0.00004   380 0.00014  */
@font-face{
  font-family:'GSF Fallback Adj';
  src:local('Helvetica Neue'),local('Helvetica'),local('Arial'),local('Liberation Sans');
  font-weight:400 700;
  size-adjust:107%;
  ascent-override:90.28%;
  descent-override:26.73%;
  line-gap-override:0%;
}
/* This one is DELIBERATELY NOT RETUNED, and that is an open item rather than a
   decision. Segoe UI and Roboto are not installed in the sandbox this was
   measured in, so the face resolves to `error` and any advance-width figure taken
   for it here is a measurement of some other font. The 107% correction above was
   made against a face that actually loaded; making the same correction here on an
   unmeasurable one would be guessing with a decimal point on it. This face sits
   first in the stack, so Windows and Android visitors are the ones still exposed
   to the pre-swap re-wrap described above. It needs one measurement on a machine
   with Segoe UI present. */
@font-face{
  font-family:'GSF Fallback Adj MS';
  src:local('Segoe UI'),local('Roboto');
  font-weight:400 700;
  size-adjust:108.78%;
  ascent-override:88.80%;
  descent-override:26.29%;
  line-gap-override:0%;
}
:root{
/* ── SURFACES ───────────────────────────────────────────────────────────────
   Neutral near-black. Chunk 5A swapped these four to the product application's
   dark ramp on the argument that one shared palette outranks hue purity on the
   marketing site alone; that swap is reverted here. The ramp it borrowed is
   built on a blue-violet base and reads cool at every step, and the site's own
   grey ramp is what the brand red was chosen against. The product keeps its
   ramp; the marketing site keeps its neutrals. This is a deliberate,
   approved reversal — do not re-swap.

   Grey channels only: every value below has R = G = B, so no surface can
   contribute a cast under any blend or alpha composite. */
--bg:#0a0a0a;
--s1:#121212;
--s2:#171717;
--s3:#1f1f1f;
/* ── TEXT ────────────────────────────────────────────────────────────────────
   The neutral ramp, and the destination for every retired green, amber and
   pink. Where hue used to separate two things, position, size, weight or a
   hairline separates them now. Re-cut with the surfaces above so the ink ramp
   and the surface ramp share one grey; R = G = B here too. */
--t1:#f2f2f2;
--t2:#9a9a9a;
/* Was #4a4a4a, then #7a7a7a. Chunk 8 specified #4a4a4a from the ramp's spacing
   rather than from a measurement, and #7a7a7a replaced it on a measurement taken
   against --bg only: 4.61:1 there, which clears AA. But --t3 does not only sit on
   --bg. It carries .rc-sl, .lb, .tr, .pd, .price-fine-print, .stacked-label,
   .fc-cap and .id-label, and every one of those sits on a card, which is --s1,
   --s2 or --s3. Measured against the real rendered background at 1440x900 and
   380x740, that was 24 elements per viewport between 4.18:1 and 4.36:1 — the
   single largest contrast failure on the page, and all of it invisible to a
   check that only tests the page background.
     #7a7a7a  bg 4.61  s1 4.36  s2 4.18  s3 3.84
     #888888  bg 5.58  s1 5.28  s2 5.06  s3 4.65
   #888888 is the first step up the same neutral axis that clears 4.5:1 on every
   surface --t3 is used on, including --s3. R = G = B, so the no-cast rule holds. */
--t3:#888888;
/* ── HAIRLINES ───────────────────────────────────────────────────────────── */
--br:rgba(var(--paper-rgb),.06);
--br-hi:rgba(var(--paper-rgb),.1);
/* ── ACCENT ──────────────────────────────────────────────────────────────────
   One red: the brand red. --acc-rgb carries the same colour as bare channels
   so every tint is one token, not twelve. --acc-lo is the 15% wash used for
   soft accent fills; the second chart series uses a 45% step of the same
   channels rather than a second hue. */
--acc:#e43531;
--acc-rgb:228,53,49;
--acc-lo:rgba(var(--acc-rgb),.15);
/* --acc-tx: THE SAME RED, LIGHTENED, FOR SMALL TEXT ON A TINTED WASH — AND FOR
   NOTHING ELSE (chunk 14D).

   THE DISTINCTION, because it is the thing that will get this wrong later:
     --acc     is the brand red. It is what fills, large text, borders, chips,
               icons, the constellation and every --acc-rgb tint are made of.
               It does not change, anywhere, for any reason.
     --acc-tx  is a TEXT-ONLY substitute, and only where red type under 24px
               sits on a surface that is itself a red wash. Never as a fill,
               never as a border, never on --bg, never on large text.

   Why it is needed at all: --acc measures 4.57 on --bg, which clears AA for
   small text with 0.07 of margin. Composite a 12-15% --acc-rgb wash under it
   and the background lifts from rgb(10,10,10) toward rgb(43,16,16), the margin
   is gone, and the same red fails. Four elements were in that position and
   nothing except a lighter red fixes them without either removing the wash or
   making the text non-red.

   Why NOT to just lighten --acc globally: on --bg, #ef5b57 is a visibly
   different, pinker red, and it would also change every fill, every border and
   the constellation, which are all at or above their thresholds already and
   were tuned to #e43531.

   MEASURED, from composited pixels rather than from the tokens — see the four
   call sites and the chunk 14 report for the before/after pairs. */
--acc-tx:#ef5b57;
/* ── NEUTRAL CHANNELS ────────────────────────────────────────────────────────
   --white is label text sitting on an accent fill; it is deliberately off pure
   white. --paper-rgb is the same colour as bare channels, for hairlines and the
   hero grid.
   --bg-rgb and --neutral-rgb are gone. They restated --bg and --t3 as channel
   triples purely so the two could be used with alpha, which meant the same
   colour was written down twice and a future edit to --bg or --t3 would have
   silently left its triple behind. The alpha uses now derive from the tokens
   themselves via color-mix, so there is one definition per colour. The two
   backgrounds that would be unusable without alpha carry a plain-colour
   fallback declaration ahead of the mix; the decorative shadows do not need
   one. */
--white:#fafafa;
--paper-rgb:250,250,250;
/* ── TYPE ────────────────────────────────────────────────────────────────────
   ONE family. --ff (Google Sans Flex) carries everything on the page.

   Google Sans Flex replaces Outfit. Outfit is a geometric sans whose lighter
   weights go thin and even at display size, which is what let the 500 rule of
   chunk 5B read as underset. Google Sans Flex holds a firmer stem at 700 and has
   a taller x-height, so the same measure carries more ink at the same size. It
   is a variable font on Google Fonts under the SIL Open Font License, free for
   commercial use, and one file serves 400 through 700.

   --fm IS NO LONGER A SECOND FAMILY. Space Mono is dropped (chunk 11B) and --fm
   now resolves to the same stack as --ff. It is kept as a token rather than being
   find-and-replaced away for two reasons: it is the single point that routes every
   one of the 20 declarations that used to be mono, so there is one place to look;
   and it still names a real distinction — --fm marks the figures and the small
   uppercase labels, which is what --fw-mono-lb and --tnum below key off.

   Mono was carrying two jobs and only ever did one of them well. It aligned
   columns of figures, which mattered; and it flagged "this is measured", which a
   second family is a heavy way to say. Google Sans Flex has a real tabular figure
   set — measured, see --tnum — so the alignment job survives the swap intact, and
   the flagging job moves to weight and tracking. */
--ff:'Google Sans Flex','Google Sans','GSF Fallback Adj MS','GSF Fallback Adj',system-ui,-apple-system,'Segoe UI',sans-serif;
--fm:'Google Sans Flex','Google Sans','GSF Fallback Adj MS','GSF Fallback Adj',system-ui,-apple-system,'Segoe UI',sans-serif;
/* Tabular figures, as a token so every figure on the page opts in the same way.
   VERIFIED BY MEASUREMENT, not assumed. Rendered width of a single glyph at 100px
   in Google Sans Flex, all ten digits:
     default (proportional)  36.75 - 64.50px, a 27.75px spread
     tabular-nums                    64.50px flat, spread 0
   And on a string the calculator actually produces:
     "$1,111,111" vs "$8,888,888"  default 339.75 vs 493.50px  (153.75px apart)
                                   tnum    580.50 vs 580.50px  (identical)
   font-feature-settings:"tnum" gives byte-identical results, so the standard
   property is enough and the fallback is not needed. No fixed-width container is
   needed either. Without this the savings figures would jump by up to 150px as a
   slider moves, which is exactly the jitter mono existed to prevent. */
--tnum:tabular-nums;
/* ── TYPE WEIGHT ─────────────────────────────────────────────────────────────
   Every font-weight on the page routes through one of these six. There are no
   literal font-weight values left in index.css or in index.html.

   Chunk 5B set all three display steps to 500 on the principle that hierarchy
   should come from scale and negative tracking alone. That principle came from
   an airy editorial reference and is wrong for this brand: the page is meant to
   read as a precision instrument, and at 500 the display type read thin and the
   sections lost their top. Weight is back in the hierarchy. This is a
   deliberate, approved reversal — do not re-flatten these to one value.

   --fw-ui is the one step that is neither display nor body: 14-18px UI labels
   and proper names (nav items, the hero badge, an integration name, a
   testimonial byline). They were 500 before this chunk and stay 500, because
   they are not display type and the reversal above is about display type. */
--fw-display:700;                                /* hero, section headline */
--fw-sub:600;                                    /* subsection + card titles */
--fw-cta:700;                                    /* CTA label */
--fw-ui:500;                                     /* small UI label, proper name */
--fw-body:400;                                   /* body, caption */
--fw-mono-em:700;                                /* figure chip sitting on a fill */
/* Small uppercase labels. They were --fw-body (400) while they were set in Space
   Mono, whose 400 is already a dense, even colour at 11-13px. Google Sans Flex at
   400 and 11px with 0.12em tracking reads noticeably lighter than the mono it
   replaces — the letterforms are narrower and the counters more open, so the same
   weight puts less ink on the line. 600 restores the label's presence at the same
   size, tracking and casing, which is what chunk 11B asks for. Not 700: 700 is
   --fw-mono-em, which is reserved for a figure chip sitting on a solid fill and
   has to hold against --acc. */
--fw-mono-lb:600;                                /* small uppercase label */
/* ── FIGURE WEIGHT AND WIDTH (chunk 14A) ─────────────────────────────────────
   Every large figure on the page rendered at 400 while display type sat at 700,
   so .roi-sub — a 23px supporting sentence at --fw-sub 600 — was heavier than
   the 72px $54,250 figure it exists to support.

   DIAGNOSIS, CONFIRMED. The cause is the one chunk 14 proposed. Space Mono
   shipped 400 and 700 only, and at 400 its numerals are already a dense, even
   colour, so a figure set in mono at 400 read as a figure. When chunk 11B
   dropped Space Mono and pointed --fm at Google Sans Flex, the twenty
   declarations that moved carried their weight values across unchanged. Every
   one of them said --fw-body. Google Sans Flex at 400 is a text weight, not a
   figure weight, so all twenty went light at once and none of them picked up a
   display token, because until now there was no figure weight token to pick up.
   This is the same failure --fw-mono-lb already fixed for the small uppercase
   labels in that chunk; it was fixed for the labels and missed for the figures.

   WHAT THE FONT ACTUALLY EXPOSES. Read from the fvar table of the binary
   Google serves, then confirmed on rendered text in the browser:
     wght  1-1000   (default 400)   -- requested 400..800, arrives as 400-1000
     wdth  25-151   (default 100)   -- NOT requested, pinned at 100
     opsz  6-144    (default 18)    -- not requested, pinned at 18
     ROND  0-100    (default 0)     -- not requested, pinned at 0
   Only what the css2 URL asks for is in the binary, which is why a wdth value
   set here would be silently inert. There is no --vs-metric token any more, and
   adding one back without also adding the axis to the URL in index.html would
   change nothing on screen.

   THE VALUES, and why these.
   800 on the largest figures, 700 on the rest. 800 puts 30.055% ink on
   "$54,250" at 72px against 700's 27.194%, +10.5%, which is the step that makes
   the figure outweigh the sentence under it. Nothing goes above 800: the axis
   runs to 1000 but 900 and 1000 are display-poster weights that would make the
   figures louder than the h1.

   THE WIDTH AXIS WAS REVERTED IN 15C, and this is the argument, because the
   design case for it was real. Chunk 14A set the figures at wdth 110, which
   widened them 12.0%, on the reasoning that "the numerals read narrow" is
   literally a width problem and that weight and width do two different jobs —
   weight supplies density, width supplies presence. That reasoning still holds.
   What it cost is 26,540 B on the latin subset, +69.1%, for seventeen figures.
   Weight 800 and the tracking below had already answered the complaint on their
   own; the width was a refinement stacked on a fix that had landed. On a phone
   on a job site that trade does not pay. If it is ever revisited, note that
   widening LOWERS ink coverage (30.055% at wdth 100, 28.86% at 110) because the
   glyph box grows faster than the stroke does, so it is not a way to get weight.

   Tracking tightens with size. Heavy numerals at loose tracking read as a
   billboard; the intent is a gauge face or a spec plate, which is heavy and
   tight. --tr-metric stays -.02em for the 12-13px chart figures that also
   use it, where -.04em would close them up. */
--fw-metric:700;                                 /* figures at --fs-metric */
--fw-metric-xl:800;                              /* --fs-metric-lg and -xl */
--tr-metric-md:-.03em;                           /* figures at --fs-metric */
--tr-metric-xl:-.04em;                           /* --fs-metric-lg and -xl */
/* ── TYPE SCALE ──────────────────────────────────────────────────────────────
   Every font-size on the page routes through one of these. There are no literal
   font-size values left in index.css or in index.html.

   Hierarchy comes from size, tracking AND weight. Sizes pair with the weight
   tokens above: display and section headline take --fw-display, subsection and
   card titles --fw-sub, body and captions --fw-body.

   Each clamp floor is the 380px value and each ceiling the 1440px value, so the
   scale is fluid between them and needs no size-specific overrides. The display
   step floors at 40px, inside the 44px ceiling the brief sets for 380px. */
/* 54px, below the brief's 60-72px display band, because measurement requires it
   and the brief allows going under when it does. Google Sans Flex is a wider face
   than Outfit — the headline sets 39.52em on one line at 700/-0.035em against
   Outfit's 32.87em at 500/-0.03em, +20.2% — so the same 1152px container holds a
   noticeably smaller size at two lines.

   Swept on the live element at 1440px in 1px steps. Two lines survive to 58px and
   break to three at 59px, so 58px is the hard ceiling, not 64px as it was under
   Outfit. Slack on the widest line at each candidate: 58px leaves 10.7px (0.9%),
   57px 30.3px (2.6%), 56px 50.0px (4.3%), 55px 69.7px (6.1%), 54px 89.4px (7.8%).
   54px is taken because it is the first step that clears the ~7% headroom this
   file already committed to for exactly this risk — a platform whose rasterisation
   rounds differently must not be able to tip line two into a third. 52px and 53px
   buy more room but cost size for no additional safety.

   It stays clearly above --fs-h2's 50px, so the hero headline is still the largest
   language type on the page. 3.8vw rather than 4.2vw so the ceiling still binds
   before 1440px (it binds at 1421px) and the step tracks the shrinking container
   below that.

   FLOOR 40px -> 38px, chunk 10A. Swept on the live element at 380x740 with the real
   font binaries served (Google Fonts is blocked in this sandbox and a substitute face
   invalidates every line count). Headline line count and the primary CTA's bottom edge
   against the 740px fold, at each candidate:

     40px  7 lines  CTA bottom 1089.9  (349.9 below the fold)   <- was shipping
     38px  5 lines  CTA bottom  994.5  (254.5 below)            <- now shipping
     36px  5 lines  CTA bottom  983.9  (243.9 below)
     32px  5 lines  CTA bottom  962.7  (222.7 below)
     30px  4 lines  CTA bottom  920.3  (180.3 below)
     24px  4 lines  CTA bottom  894.9  (154.9 below)
     22px  3 lines  CTA bottom  863.1  (123.1 below)
     18px  3 lines  CTA bottom  850.4  (110.4 below)
     16px  2 lines  CTA bottom  827.0  ( 87.0 below)

   NO display size puts the CTA above the fold, and none can. The headline occupies
   296.7px of the 349.9px deficit at 40px, so even a headline of zero height leaves the
   CTA at 793.2px — still 53.2px below. The binding constraint is not the headline: it
   is .hero-d, which sets 900 characters as 17 lines and 504.7px at this width, 68% of
   the fold on its own. That is a copy-length decision, not a type-scale one, and it is
   out of this chunk's scope.

   38px is taken because 40 -> 38 is a genuine two-line cliff (7 -> 5 lines, 95.4px
   recovered) for a 5% size reduction. Every step below 38 buys far less per pixel
   surrendered and none of them reaches the goal, so there is no measurement requiring
   them. Still inside the v2 scale's 44px mobile display cap.

   CEILING 54 -> 58, SLOPE 3.8vw -> 4.8vw (chunk 20). The headline is 32 characters
   now instead of 86 and sits in a 575.7px track, so the size is set by the longer of
   its two lines rather than by the fold. MEASURED on the real face, "operations
   manager." at 1440x900:
     56px  533.44   58px  553.77   60px  574.11   64px  614.77
   The track is 575.7px, so 60px is the last size that fits and it fits by 1.59px.
   58px is taken instead: it clears by 21.9px, which is the margin that survives the
   hinting differences between platforms, and 60px would sit only 8px under the 50px
   section headline once .stitle is at its own ceiling. 4.8vw holds the same
   one-line-fits relationship down the range — at 1025px the track is 488.5px and
   49.2px sets the second line at 468.9px — and the 38px floor is unchanged, so
   nothing about the mobile step moves. Only .hero-t consumes this token. */
--fs-display:clamp(2.375rem,4.8vw,3.625rem);     /* 38 -> 58  hero */
/* 50px, down from 52px, to sit inside the brief's 40-50px section band. 3.85vw
   keeps the ceiling binding before 1440px. Google Sans Flex at 700/-0.025em sets
   the longest section headline at 1154.3px against the 1152px container, so the
   two longest headlines take a second line here where Outfit at 500 held them on
   one — that is the bulk of the page-height growth this chunk adds. */
--fs-h2:clamp(2rem,3.85vw,3.125rem);             /* 32 -> 50  section headline */
--fs-h3:clamp(1.5rem,2.2vw,1.875rem);            /* 24 -> 30  subsection */
--fs-h4:clamp(1.25rem,1.6vw,1.5rem);             /* 20 -> 23  card title */
--fs-body:1.125rem;                              /* 18       body */
--fs-small:.875rem;                              /* 14       small / caption */
/* CTA labels. WCAG's large-text exemption, which drops the threshold to 3:1 and
   lets --white stay on --acc, starts at 18pt (24px) or 14pt bold (18.66px). 18px
   is a third of a pixel short of that at bold, so the step stays 19px — it
   satisfies the 18px minimum and clears 18.66px.

   CLOSED in chunk 9. The exemption needs 18.66px *and bold*, and --fw-cta was
   600, which is semibold. At 19px/700 the label is large text and --white on
   --acc passes on 3:1 (measured 4.15). At 19px/600 it was no longer large text,
   so the same pairing was judged against 4.5:1 and 4.15 failed. The size half
   of the reasoning above always held; the weight half did not. --fw-cta is now
   700, which restores the pass and changes nothing else. */
--fs-cta:1.1875rem;                              /* 19       CTA label */
/* Mono steps. The metric ladder is what carries the figures now that chunk 4
   removed their gradients: scale does the work colour used to do. */
--fs-metric-xl:clamp(2.75rem,5vw,4.5rem);        /* 44 -> 72  the $54,250 figure */
--fs-metric-lg:clamp(2.25rem,3.2vw,3rem);        /* 36 -> 48  price, grand total */
--fs-metric:clamp(1.5rem,2vw,1.75rem);           /* 24 -> 28  calculator figures */
--fs-metric-sm:1.125rem;                         /* 18       table values */
--fs-mono:.8125rem;                              /* 13       mono label */
--fs-mono-sm:.75rem;                             /* 12       small label */
/* 10px, and it exists for exactly one place: the stacked chart's totals and x-axis
   labels below 480px, where 12px does not fit the column the viewport allows. See
   the D1 block for the measurement that sets it. */
--fs-mono-xs:.625rem;                            /* 10       chart label, <480px */
/* Glyph sizes. These size an icon, not text, and are kept apart from the type
   scale for that reason. The icon chunk replaces the glyphs they size. */
--fs-glyph-xl:2rem;
--fs-glyph:1.375rem;
--fs-glyph-sm:.8125rem;
--fs-glyph-close:2.5rem;
/* ── ICONS ───────────────────────────────────────────────────────────────────
   One family: Tabler outline, inlined as <svg>, no package and no CDN. Every
   glyph is stroke-width 1.75 on a 24x24 box with stroke:currentColor and
   fill:none, so an icon takes its colour from whatever token its container
   already uses and cannot introduce an off-palette value.
   Three glyphs live on pseudo-elements, where an <svg> child is impossible.
   Those use the same Tabler path as a mask: a mask samples alpha only, so the
   #fff stroke inside the data URI is never painted — the paint comes from
   background-color:currentColor, which keeps the same token inheritance the
   inline icons get. */
--ic-arrow-right:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l14 0'/%3E%3Cpath d='M13 18l6 -6'/%3E%3Cpath d='M13 6l6 6'/%3E%3C/svg%3E");
--ic-check:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l5 5l10 -10'/%3E%3C/svg%3E");
/* ── TRACKING ────────────────────────────────────────────────────────────────
   Negative on display type, where it does the job weight used to do; positive
   only on mono labels, where the wide advance needs it. */
--tr-display:-.035em;
--tr-h2:-.025em;
--tr-h3:-.015em;
--tr-metric:-.02em;
--tr-mono:.14em;
/* ── LINE HEIGHT ─────────────────────────────────────────────────────────── */
--lh-display:1.06;
--lh-h2:1.1;
--lh-h3:1.25;
--lh-body:1.65;
--lh-mono:1.4;
/* ── RADIUS ──────────────────────────────────────────────────────────────────
   Three steps, and nothing else. Every radius on the page routes through one of
   these three tokens. --rd-0 is hairline-bounded blocks, full-bleed panels,
   section dividers and image frames. --rd is contained elements: buttons,
   inputs, pricing tier cards, modals. --rd-pill is chips, badges, small tags
   and toggles. The one thing that stays a literal is 50% on a true circle
   (avatars, the AI orb, dots, slider thumbs) — that is geometry, not a radius
   choice, and it does not belong on this scale. */
--rd-0:0;
--rd:10px;
--rd-pill:9999px}
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
/* Icons are sized in em against the type beside them, so they follow the type
   scale instead of holding a second set of pixel values. The negative
   vertical-align pulls them off the baseline onto the type's optical centre —
   the containers that are flex handle their own centring and are excluded. */
.ie svg,.fi svg,.ai-fi svg,.ck svg,.ts svg,.ft-s svg,.id-arrow svg,.tb-i .n svg,.mob-m a svg,.ft-c a svg,.section p svg{width:1em;height:1em;flex-shrink:0}
.ck svg,.ts svg,.tb-i .n svg,.mob-m a svg,.ft-c a svg,.section p svg{vertical-align:-.145em}
html{scroll-behavior:smooth;color-scheme:dark}
body{font-family:var(--ff);font-size:var(--fs-body);font-weight:var(--fw-body);background:var(--bg);color:var(--t1);line-height:var(--lh-body);overflow-x:hidden;-webkit-font-smoothing:antialiased}
/* Anchors with no colour of their own used to render the browser's link blue
   (#0000ee); under color-scheme:dark they would render its dark-mode blue
   (#9e9eff) instead. Neither is in the palette. This is the lowest-specificity
   rule on the page, so it catches only those anchors and cannot override any
   class-based or inline anchor colour. Same reason for the range inputs: their
   dark-mode default control colour is a warm grey. */
a{color:inherit}
.container{max-width:1200px;margin:0 auto;padding:0 24px}
.section{padding:120px 0;position:relative}
#team{border-top:1px solid var(--br);border-bottom:1px solid var(--br)}
.slabel{font-family:var(--fm);font-size:var(--fs-mono);font-weight:var(--fw-mono-lb);letter-spacing:var(--tr-mono);text-transform:uppercase;color:var(--acc);margin-bottom:16px;display:inline-flex;align-items:center;gap:10px;line-height:var(--lh-mono)}
.slabel::before{content:'';width:32px;height:2px;background:var(--acc);display:inline-block}
.stitle{font-family:var(--ff);font-size:var(--fs-h2);font-weight:var(--fw-display);letter-spacing:var(--tr-h2);line-height:var(--lh-h2);margin-bottom:20px}
.ssub{font-size:var(--fs-body);color:var(--t2);max-width:640px;line-height:var(--lh-body)}
/* 19px/700, not 15.2px/600 — see --fs-cta. This is the one place weight is used
   for something other than hierarchy, and it is there for the contrast
   exemption, not for emphasis. */
.btn{display:inline-flex;align-items:center;gap:10px;font-family:var(--ff);font-weight:var(--fw-cta);font-size:var(--fs-cta);padding:16px 28px;border-radius:var(--rd);border:none;cursor:pointer;text-decoration:none;transition:all .35s cubic-bezier(.4,0,.2,1)}
.btn-p{background:var(--acc);color:var(--white)}
.btn-p:hover{transform:translateY(-2px)}
.btn-s{background:transparent;color:var(--t1);border:1.5px solid var(--br-hi);backdrop-filter:blur(10px)}
.btn-s:hover{border-color:var(--acc);color:var(--acc);background:var(--acc-lo);transform:translateY(-2px)}
/* .btn-sm reduces padding only. It cannot reduce the label below --fs-cta:
   two of the four buttons using it are .btn-p, so their --white label sits on
   an --acc fill and depends on the large-text exemption to pass. */
.btn-sm{padding:12px 24px}
.btn-a::after{content:'';width:1em;height:1em;flex:0 0 auto;background-color:currentColor;-webkit-mask:var(--ic-arrow-right) center/contain no-repeat;mask:var(--ic-arrow-right) center/contain no-repeat;transition:transform .3s}
.btn-a:hover::after{transform:translateX(4px)}

/* NAV */
/* 12px + a 44px logo + 12px = 68px, inside the direction document's preferred
   64-72px band and well under its 80px cap. It was 280px: 20 + a 240px logo + 20. */
.nav{position:fixed;top:0;left:0;right:0;z-index:1000;padding:12px 0;transition:all .4s}
.nav.scrolled{background:var(--bg);background:color-mix(in srgb,var(--bg) 92%,transparent);backdrop-filter:blur(20px) saturate(180%);border-bottom:1px solid var(--br);padding:10px 0}
.nav-in{max-width:1200px;margin:0 auto;padding:0 24px;display:flex;align-items:center;justify-content:space-between}
.nav-logo{display:flex;align-items:center;text-decoration:none}
/* The crop box for the letterboxed logo file. cortex-logo.svg carries a 3.46:1
   logotype centred inside a square canvas, so a plain height on the <img> sizes the
   empty transparency rather than the ink. Fixing the box at the ink height and letting
   align-items:center overflow the taller <img> symmetrically crops both empty bands
   exactly, with no magic negative offset — and it costs nothing when the 44px
   .nav-logo-mark takes over below 768px, because the mark already fits the box.
   Scoped to .nav on purpose: the footer reuses .nav-logo for a square icon that must
   not be cropped. */
.nav .nav-logo{height:44px;overflow:hidden}
.nav-logo-mark{display:none}
.nav-links{display:flex;align-items:center;gap:36px;list-style:none}
.nav-links a{text-decoration:none;color:var(--t2);font-size:var(--fs-small);font-weight:var(--fw-ui);transition:color .3s}
.nav-links a:hover{color:var(--t1)}
.nav-cta{display:flex;align-items:center;gap:12px}
/* With the logo down to 44px the nav's height floor became the CTA button, not the
   logo: a 19px label inheriting body's 1.65 gives a 31px line box, so the button stood
   53px tall and held the nav at 77px. --fs-cta cannot drop (it is what earns the
   large-text contrast exemption for --white on --acc), so the paragraph line-height
   goes instead — 1.2 is the right value for a single-line button label. Scoped to the
   nav so no other CTA on the page changes size. */
.nav-cta .btn-sm{padding:10px 18px;line-height:1.2}
.nav-login{text-decoration:none;color:var(--t2);font-family:var(--ff);font-weight:var(--fw-ui);font-size:var(--fs-small);transition:color .3s;white-space:nowrap}
.nav-login:hover{color:var(--acc)}
/* 7px, not 12px. The three bars plus their collapsed margins are a 30px content box,
   so 12px of padding made the button 54px and that, not the logo, was the mobile nav's
   height floor. At 7px the button measures exactly 44x44 — the minimum tap target is
   unchanged, the surplus above it is gone. */
.nav-tog{display:none;background:none;border:none;cursor:pointer;padding:7px;min-width:44px;min-height:44px;position:relative;z-index:1001}
.nav-tog span{display:block;width:24px;height:2px;background:var(--t1);margin:6px 0;border-radius:var(--rd-pill)}

/* HERO */
/* Two min-heights on purpose. Browsers without dvh (Safari < 15.4, Chrome < 108)
   cannot parse the second line and keep the first; browsers with dvh take the
   second. Removing the 100vh line leaves older browsers with no full-height hero. */
/* CONSTELLATION BRAIN
   z-index:-1, not a positive z-index with the page content lifted above it.
   html carries no background, so body's --bg propagates to the root painting
   surface and is painted before any stacking context; a negative-z-index fixed
   element paints after that surface but before every in-flow block background,
   inline box and positioned element on the page. The brain therefore sits above
   the page's black and beneath all copy without one existing rule changing.
   Lifting the content instead would have meant adding position/z-index to every
   section, which is exactly the kind of change that moves things.
   Sections that carry their own opaque surface (--s1 on .roi-s, .test-s, #team,
   .footer) hide it while it is behind them; .hero and .plan-s are transparent,
   which is where it rests and where it reassembles. */
#cortexField{position:fixed;inset:0;width:100%;height:100%;z-index:-1;pointer-events:none}

/* align-items:flex-start, not center (15A). Centring a 402px block inside a 100dvh
   flex container put 201px of dead space above the eyebrow and 201px below it, and
   the 96px padding-top was never the cause: it clears the 68.8px fixed nav, and
   deleting all of it would have recovered 48px of the 228px gap, because centring
   hands half of any padding change straight back. Measured at 1440x900 before the
   change: content box 804px, container 401.97px, free space 402.03px, content top
   96 + 201.02 = 297.02, nav bottom 68.80, gap 228.38.
   Starting the content instead of centring it makes the distance from the top of
   the viewport a constant at every window height, rather than half of whatever is
   left over — a 1440x1400 window had a 478px gap under the old rule. */
/* HEIGHT IS SET BY THE CONSTELLATION, NOT BY THE VIEWPORT (chunk 20).
   100vh/100dvh left 338.03px between the bottom of the CTA row and the trust band at
   1440x900 and 249.41px at 1280x800. The whole of it was overshoot: the hero's flow
   content measured 561.97px at 1440x900 (96px padding, 64px container margin, 401.97px
   container) inside a 900px box, with align-items:flex-start holding the copy at the
   top. Nothing was added below it and no negative margin is involved here — the height
   itself was wrong.
   It could not simply shrink to the copy WHILE THE CLOUD'S CEILING WAS THE HEADLINE.
   The right track holds the constellation, and with the ceiling at .hero-hd the fit
   was bound by this element's bottom edge at every wide viewport, so hero height and
   cloud size were one number and every pixel taken off the hero came off the brain.
   Chunk 21c moved the ceiling to the bottom of the nav and that is no longer true:
   from roughly 1300px up the fit is bound by the width left of the copy column
   instead, and the hero can come down without touching the cloud.
   The clamp stays width-driven because the fit is width-driven, which also keeps the
   tall-window bug retired — 1440x1400 gets the same hero as 1440x900.

   CHUNK 21D: 54vw -> 48vw, min 700 -> 690, max 820 unchanged.
   48vw is the smallest round coefficient that keeps the horizontal term binding at
   1440. Below 690.15px of hero the vertical term takes over again and the cloud
   starts shrinking with the hero; 48vw gives 691.2px, 1.05px of margin, and lands the
   gap at 120.75px against a 120px target. Shaving to 47.94vw would close that 0.75px,
   but the window between "gap under 120" and "still decoupled" is 0.29px wide at
   1440 and it moves with viewport width — 47.94vw is already re-coupled at 1400px.
   0.75px of gap does not buy a rule that only holds at one width.
   The max stays 820 deliberately. 1920 is the one viewport where the cloud is still
   bound by the hero — its horizontal fit wants an 899px hero, which the clamp will
   not give and which would open a 328px gap — so lowering the max is the one change
   here that would visibly shrink the brain.
   THE MIN STOPS AT 690 AND NOT LOWER, and the reason is 380px, not any wide width.
   The narrow branch has no bottom padding, so once the min stops binding the hero is
   exactly as tall as its own copy and the CTA button's bottom edge lands flush on the
   trust band's top border. 660 did that: hero 669.33, zero clearance. 690 keeps
   20.67px there. It costs nothing above 1437.5px, where 48vw takes over anyway.

   MEASURED, gap from the hero container's bottom to the trust band's top, and the
   cloud's fitted scale, before this chunk -> after:
     1440x900   hero 777.59 -> 691.2  gap 207.16 -> 120.75  scale 313.65 -> 313.65
     1280x800   hero 700    -> 690    gap 129.56 -> 119.56  scale 277.61 -> 277.61
     1920x1080  hero 820    -> 820    gap 249.56 -> 249.56  scale 380.93 -> 380.93
     1440x1400  hero 777.59 -> 691.2  gap 207.16 -> 120.75  scale 313.65 -> 313.65
     380x740    hero 700    -> 690    gap  30.67 ->  20.67  scale  91.20 ->  91.20
   The cloud is bound by the horizontal term, and so unaffected by this rule, from
   1025px to roughly 1740px. Above that the hero's bottom edge binds again, which is
   the floor doing its job rather than coupling: it is what keeps the cloud from
   hanging across the trust band. */
.hero{min-height:clamp(690px,48vw,820px);display:flex;align-items:flex-start;position:relative;padding-top:96px;overflow:hidden}
.hero-bg{position:absolute;inset:0;z-index:0}
.hero-grid{position:absolute;inset:0;background-image:linear-gradient(rgba(var(--paper-rgb),.02) 1px,transparent 1px),linear-gradient(90deg,rgba(var(--paper-rgb),.02) 1px,transparent 1px);background-size:60px 60px;mask-image:radial-gradient(ellipse 80% 60% at 50% 40%,var(--white) 30%,transparent 100%)}
/* One column of copy, not two rows (chunk 20). Badge, headline, subtext and CTAs all
   sit in track 1; track 2 is left empty for the constellation and is now empty from
   the top of the container rather than from below the headline. That is the whole
   point of the change: the cloud's ceiling is read off .hero-hd, so a headline
   spanning both tracks cost it 207px of height it can now use.
   Track 1 is 1.16fr against 1fr, which is 575.7px of the 1151.98px content area at
   every width from 1200px up — 49.97%, the top of the 45-50% band. It is at the top of
   that band because the band's bottom does not work: at 45% the track is 518.39px and
   the largest size whose second line still fits is 54px, under the 56px floor. Wider
   than 50% takes width straight off the constellation, which is fitted to what is left
   of the viewport. The row gap stays 0 because .hero-t carries a 24px margin-bottom;
   only the column gap is 80px. */
/* The 64px is the space between the nav and the eyebrow, and it lives here rather
   than in .hero's padding because that padding is capped at 96px and is spoken for:
   96px is nav clearance, 64px is the gap. Content therefore starts at 160px from the
   top of the viewport at every width and every window height. */
.hero .container{position:relative;z-index:1;display:grid;grid-template-columns:1.16fr 1fr;gap:0 80px;align-items:center;margin-top:64px}
.hero-hd{grid-column:1}
.hero-c{grid-column:1;max-width:620px}
/* 14px --acc on the --acc-lo wash measured 4.10. --acc-tx, one of the four. */
.hero-badge{display:inline-flex;align-items:center;gap:8px;background:var(--acc-lo);border:1px solid rgba(var(--acc-rgb),.2);border-radius:var(--rd-pill);padding:8px 20px 8px 12px;font-size:var(--fs-small);font-weight:var(--fw-ui);color:var(--acc-tx);margin-bottom:28px;animation:fu .8s ease-out both}
/* Display step: 800 down to 500, and the -1.5px literal becomes -.03em so the
   tracking scales with the size instead of over-tightening the 40px mobile step. */
.hero-t{font-family:var(--ff);font-size:var(--fs-display);font-weight:var(--fw-display);line-height:var(--lh-display);letter-spacing:var(--tr-display);margin-bottom:24px;animation:fu .8s .15s ease-out both}
.hero-t .gt{color:var(--t1)}
.hero-d{font-size:var(--fs-body);color:var(--t2);line-height:var(--lh-body);margin-bottom:40px;max-width:480px;animation:fu .8s .3s ease-out both}
.hero-acts{display:flex;gap:16px;flex-wrap:wrap;animation:fu .8s .45s ease-out both}

/* TRUST BAND. Deliberately not a card family: one band, hairline top and bottom,
   three inline values separated by rules. The section immediately below is the card
   grid, and three cards here would make it a run of two. */
.trust-band{border-top:1px solid var(--br);border-bottom:1px solid var(--br);padding:28px 0}
.tb-row{display:flex;align-items:center;gap:24px}
.tb-i{display:flex;flex-direction:column}
/* Left in Outfit on purpose. Two of the three .tb-i values are numbers
   ("Since 2013", "4.5 stars") but the third is the word "Optional", and mono on one
   instance of a repeated component and not the others reads as a mistake.
   Consistency inside the component wins over the mono rule here. */
.tb-i .n{font-family:var(--ff);font-size:var(--fs-h4);font-weight:var(--fw-sub);letter-spacing:var(--tr-h3);line-height:var(--lh-h3);font-variant-numeric:var(--tnum)}
.tb-i .l{font-size:var(--fs-small);color:var(--t3);font-weight:var(--fw-body)}
.tb-div{width:1px;height:40px;background:var(--br-hi)}

/* fu is NOT dead: it is the shared entrance animation for the badge, headline,
   body copy and CTAs. */
@keyframes fu{from{opacity:0;transform:translateY(24px)}to{opacity:1;transform:translateY(0)}}

/* ROI */
/* ── TRANSPARENT FULL-WIDTH SECTIONS (chunk 11C) ─────────────────────────────
   Every full-width band on this page used to paint an opaque --s1 to separate
   itself from its neighbours. With the constellation now dispersing into an
   ambient field that runs the whole length of the document behind a fixed
   canvas at z-index:-1, each of those fills cut the field into a black band —
   the field appeared, vanished for a section, reappeared, and read as a bug.
   The fills come off and hairline rules in --br do the separating instead. --br
   is rgba(250,250,250,.06), so a rule reads as an edge without occluding the
   field the way a 100%-opaque #121212 does.
   Bounded surfaces STAY: .roi-cc, .roi-calc, .rc-r, .rc-total, .pc, .tc and
   .mob-m all keep their fills. A panel is what tells someone a slider is
   touchable, and a pricing tier that floats on the page background stops
   reading as a thing you choose between. The rule is about full-width bands,
   not about every surface. */
.roi-s{border-top:1px solid var(--br);border-bottom:1px solid var(--br);overflow:hidden}
/* align-items:start, not center. Centring split the height difference between the two
   columns evenly, which pushed the section's own eyebrow and $54,250 figure 158px below
   the top of the section and left the same 158px stranded under the narrative column's
   CTA — the gap chunk 6 flagged once the static tables came out of that column. Starting
   both columns on the same line is the rhythm every other section on the page uses: the
   eyebrow sits at the section's top edge. See the chunk 7 report for the residual
   imbalance, which is a composition problem rather than an alignment one. */
/* Row gap is 48px, column gap stays 80px. Until the CTA became its own grid
   child there was only one row, so the row value was never drawn; 48px is the
   figure this section already uses for stacked spacing (it is the collapsed
   single-column gap below 1024px, and sits with .roi-hours' 32px and
   .calc-grid's 40px), where the inherited 80px would have read as a hole. */
.roi-s > .container:first-child{display:grid;grid-template-columns:1fr 1fr;gap:48px 80px;align-items:start}
/* Spans every track, then centres. The button is inline-flex, so centring the
   line box is what positions it and it keeps its content width. */
.roi-s > .container:first-child > .roi-cta{grid-column:1 / -1;text-align:center}
/* The page's largest metric. It lost its gradient in chunk 4 and went flat;
   Space Mono at 72px is what carries it now. 900 -> 400: at this size the
   figure does not need weight. */
.roi-big{font-family:var(--fm);font-size:var(--fs-metric-xl);font-weight:var(--fw-metric-xl);letter-spacing:var(--tr-metric-xl);line-height:1;color:var(--t1);margin-bottom:8px;font-variant-numeric:var(--tnum)}
.roi-sub{font-family:var(--ff);font-size:var(--fs-h4);font-weight:var(--fw-sub);letter-spacing:var(--tr-h3);line-height:var(--lh-h3);margin-bottom:24px}
/* min-width:0 so the sideways scroll on .stacked-chart can actually happen. A grid item
   defaults to min-width:auto, which is min-content — the chart's 384px min-content was
   propagating up and widening the whole card to 442px instead, which at 380px ran 86px
   past the viewport and was hidden only by body{overflow-x:hidden}. */
.roi-charts{display:flex;flex-direction:column;gap:32px;min-width:0}
/* Keeps the .roi-charts class: the animation observer selects on it. */
.roi-hours{margin-top:32px}
.roi-cc{background:var(--s2);border:1px solid var(--br-hi);border-radius:var(--rd);padding:28px;position:relative;overflow:hidden}
.roi-cc::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:var(--acc)}
.roi-ct{font-family:var(--ff);font-size:var(--fs-h4);font-weight:var(--fw-sub);letter-spacing:var(--tr-h3);line-height:var(--lh-h3);margin-bottom:24px}
.ann-b{width:100%;border-radius:var(--rd-0);background:var(--acc);transition:height 1.5s cubic-bezier(.4,0,.2,1);position:relative;min-width:40px}
.ann-b.anim{height:0!important}
.roi-bc{display:flex;flex-direction:column;gap:16px}
.roi-bi{display:flex;align-items:center;gap:16px}
.roi-bll{min-width:118px;font-family:var(--fm);font-size:var(--fs-mono);font-weight:var(--fw-body);letter-spacing:var(--tr-mono);color:var(--t2);text-align:right;line-height:var(--lh-mono);font-variant-numeric:var(--tnum)}
.roi-bco{flex:1;height:36px;border-radius:var(--rd-0);overflow:hidden;position:relative}
.roi-bar{height:100%;border-radius:var(--rd-0);transition:width 1.5s cubic-bezier(.4,0,.2,1)}
.roi-bar.anim{width:0!important}

/* Calculator */
.calc-grid{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:40px}
.roi-calc{background:var(--s2);border:1px solid var(--br-hi);border-radius:var(--rd);padding:28px 24px;text-align:center;position:relative;overflow:hidden}
.roi-calc.primary{border-color:var(--acc)}
.rc-badge{display:inline-block;font-family:var(--fm);font-size:var(--fs-mono-sm);font-weight:var(--fw-mono-lb);letter-spacing:var(--tr-mono);text-transform:uppercase;padding:5px 12px;border-radius:var(--rd-pill);margin-bottom:12px;position:relative;line-height:var(--lh-mono)}
/* The lowest reading on the page before 14D: 12px --acc on a 12% --acc-rgb
   fill that itself sits on the primary calculator's tint, composited
   rgb(48,27,26), measured 3.74. The border stays --acc; only the type moves. */
.rc-badge.save{background:rgba(var(--acc-rgb),.12);color:var(--acc-tx)}
.rc-badge.earn{background:rgba(var(--paper-rgb),.06);color:var(--t1)}
.rc-t{font-family:var(--ff);font-size:var(--fs-h4);font-weight:var(--fw-sub);letter-spacing:var(--tr-h3);line-height:var(--lh-h3);margin-bottom:6px;position:relative}
.rc-d{font-size:var(--fs-small);color:var(--t2);margin-bottom:18px;position:relative;line-height:var(--lh-body)}
.rc-sw{position:relative;margin-bottom:16px;z-index:2}
.rc-sl{font-size:var(--fs-small);color:var(--t3);margin-bottom:8px;font-weight:var(--fw-body);position:relative}
/* The live team-size figure inside the slider label: a measured value, so mono. */
.rc-sl span{font-family:var(--fm);font-weight:var(--fw-mono-em);color:var(--t1);font-size:var(--fs-metric-sm);font-variant-numeric:var(--tnum)}
input[type=range]{-webkit-appearance:none;width:100%;height:8px;background:var(--s1);color:var(--t1);border-radius:var(--rd);outline:none;position:relative;z-index:2}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:28px;height:28px;border-radius:50%;background:var(--acc);cursor:pointer;border:3px solid var(--t1)}
input[type=range]::-moz-range-thumb{width:28px;height:28px;border-radius:50%;background:var(--acc);cursor:pointer;border:3px solid var(--t1)}
/* The revenue field only ever holds a number, so it is mono too — and mono keeps
   the digits from shifting sideways as the user types. */
.rc-input{width:100%;padding:12px 14px;background:var(--s1);border:1px solid var(--br-hi);border-radius:var(--rd);color:var(--t1);font-family:var(--fm);font-size:var(--fs-metric-sm);font-weight:var(--fw-body);text-align:center;outline:none;transition:border-color .3s;position:relative;z-index:2;font-variant-numeric:var(--tnum)}
.rc-input:focus{border-color:var(--acc)}
.rc-input::placeholder{color:var(--t3);font-weight:var(--fw-body)}
.rc-res{display:grid;grid-template-columns:1fr 1fr;gap:10px;position:relative}
.rc-res.tri{grid-template-columns:repeat(3,1fr)}
.rc-r{background:var(--s1);border-radius:var(--rd);padding:16px 8px}
/* The six calculator result figures. 28px at weight 700: these are the numbers
   the section exists to show, and after chunk 4 removed their gradients, weight,
   tracking and scale are all that say so. */
.rc-r .v{font-family:var(--fm);font-size:var(--fs-metric);font-weight:var(--fw-metric);letter-spacing:var(--tr-metric-md);line-height:1.15;color:var(--acc);font-variant-numeric:var(--tnum)}
.rc-r.green .v{color:var(--t1)}
.rc-r .lb{font-family:var(--fm);font-size:var(--fs-mono-sm);font-weight:var(--fw-mono-lb);color:var(--t3);margin-top:6px;text-transform:uppercase;letter-spacing:var(--tr-mono);line-height:var(--lh-mono)}
.rc-total{background:var(--s1);border:1px solid var(--br-hi);border-radius:var(--rd);padding:16px;margin-top:16px;position:relative}
.rc-total .lb{font-family:var(--fm);font-size:var(--fs-mono);color:var(--t2);text-transform:uppercase;letter-spacing:var(--tr-mono);font-weight:var(--fw-mono-lb);margin-bottom:4px;line-height:var(--lh-mono)}
.rc-total .v{font-family:var(--fm);font-size:var(--fs-metric);font-weight:var(--fw-metric);letter-spacing:var(--tr-metric-md);line-height:1.15;color:var(--t1);font-variant-numeric:var(--tnum)}
.combo-card{margin-top:20px}
/* Total Impact is the one calculator figure set at --fs-metric-lg rather than
   --fs-metric (the size override is inline on the element, and is a token). It
   belongs to the largest-figure group for weight, width and tracking as well,
   so it does not sit at 700 next to the $54,250 and $127K it totals. */
#comboTotal{font-weight:var(--fw-metric-xl);letter-spacing:var(--tr-metric-xl)}
.combo-card .rc-res.tri{gap:16px}
/* Stacked bar chart
   A TWO-ROW GRID, NOT A FLEX COLUMN, and it has to stay one. Row 1 is the plot and
   carries a definite height, so a bar's inline percentage means its share of the plot
   and nothing else. As a flex column the percentage resolved against the box including
   the x-axis label, and flex-shrink then capped every tall bar at the same height —
   100% and 81% came out 0.32px apart, representing $271,250 against $189,875. A grid
   item is not shrunk to fit its track, which is what makes 100% reachable.
   WHY --plot IS 352px. The smallest bar is 9.39% of the plot and splits 55.35/44.65
   between its two segments, so the shorter segment is 4.19% of the plot. Its label is
   12px mono at line-height 1.2, which needs 14.4px. 14.4 / .0419 = 344px is the floor;
   352px takes it with a little margin. Anything shorter clips a figure, and the figures
   are frozen copy. A 10.6x data range in a linear chart costs height — that is the
   honest price of the smallest bar being 9.39% and not 22%. */
.stacked-chart{display:flex;align-items:flex-end;gap:16px;padding-top:32px;overflow-x:auto;overflow-y:hidden}
/* flex:1 0 64px, not flex:1. 64px is the width of the widest total label ("$271,250"
   at 60.5px) plus breathing room, so a total can no longer be wider than the bar it
   labels and reach across into a neighbouring column — which is what the crowding at
   380px was. Groups still grow to fill the card when there is room; they just never
   shrink below the label. Below roughly 400px of card the chart scrolls sideways
   instead, which is visible and recoverable, where the old behaviour was to overflow
   .roi-cc and be silently clipped. */
.stacked-group{flex:1 0 64px;display:grid;grid-template-rows:352px auto;row-gap:6px;justify-items:center;align-content:end}
/* The clip lives on .stacked-fill, not on .stacked-bar. It has to live
   somewhere: .stacked-seg carries min-height:22px, so while the bar grows from
   height:0 the two segments stand 44px taller than their parent and would spill
   out of the top of the bar. But the same clip on .stacked-bar also ate
   .stacked-total, which sits at top:-28px — that is why none of the five totals
   have ever been visible. Splitting the two jobs keeps the segments clipped and
   lets the total out. .stacked-bar keeps its class, its inline height, its
   transition, and its .anim toggle, so the JS and the animation are untouched. */
/* THE REVEAL IS A WIPE, NOT A GROWTH (13D).
   .stacked-bar.anim used to be height:0 with a 1.5s height transition, so the
   bars grew into place. Height is a layout property: every frame of that
   transition emitted a layout-shift entry for the bar and for its .stacked-total,
   and those frames were the whole of the page's cumulative layout shift outside
   the hero — measured 0.0090 of 0.0090 at 380x740 and 0.0013 of 0.0092 at 768.
   The bars now sit at their real height from the start and the fill is revealed
   by animating clip-path on .stacked-fill instead. clip-path is a paint property:
   it produces the same upward wipe, does not resize anything, and generates no
   layout shift at all. The total fades in over the tail of the wipe so it does
   not simply sit there ahead of its bar.
   The JS is untouched: it still adds and removes .anim on .stacked-bar, and the
   class, the inline height and the selector string are all unchanged. */
.stacked-bar{width:100%;border-radius:var(--rd-0);position:relative;min-width:44px;display:flex;flex-direction:column;justify-content:flex-end;align-self:end}
.stacked-bar.anim .stacked-fill{clip-path:inset(100% 0 0 0)}
.stacked-bar.anim .stacked-total{opacity:0}
.stacked-fill{flex:1 1 0;min-height:0;width:100%;display:flex;flex-direction:column;justify-content:flex-end;border-radius:var(--rd-0);overflow:hidden;clip-path:inset(0 0 0 0);transition:clip-path 1.5s cubic-bezier(.4,0,.2,1)}
/* Split by fill, not by size. save-seg is a solid --acc fill, so its 10px label
   goes near-black (--bg on --acc = 4.57 vs --white on --acc = 4.15). earn-seg is
   the 45% step of the same channels, which composites to #732523 over --s2:
   --white on that measures 9.99 and --bg only 1.89, so earn-seg keeps --white.
   The same treatment on both would break one of them.
   Re-measured after chunk 8A moved --s2 to #171717; the composite and both
   ratios moved with it, and the split still lands the same way. */
/* min-height:22px is gone. It was there to keep the two labels visible, but it also
   meant the smallest bar's segments were sized by the minimum rather than by their
   values — the bar could not be proportional while it was in force. Segments divide the
   fill strictly by their flex values now. line-height drops from body's inherited 1.65
   to 1.2, which is right for a numeric chip on a fill and is what lets the plot be
   352px instead of 426px. */
.stacked-seg{width:100%;display:flex;align-items:center;justify-content:center;font-family:var(--fm);font-weight:var(--fw-mono-em);font-size:var(--fs-mono-sm);line-height:1.2;color:var(--white);font-variant-numeric:var(--tnum)}
.stacked-seg.save-seg{background:var(--acc);color:var(--bg)}
.stacked-seg.earn-seg{background:rgba(var(--acc-rgb),.45);box-shadow:inset 0 1px 0 rgba(var(--paper-rgb),.16)}
.stacked-total{position:absolute;top:-28px;left:50%;transform:translateX(-50%);font-family:var(--fm);font-weight:var(--fw-mono-em);font-size:var(--fs-mono);color:var(--t1);white-space:nowrap;letter-spacing:var(--tr-metric);font-variant-numeric:var(--tnum);opacity:1;transition:opacity .5s ease-out .55s}
.stacked-label{font-family:var(--fm);font-size:var(--fs-mono-sm);color:var(--t3);font-weight:var(--fw-body);text-align:center;line-height:var(--lh-mono);font-variant-numeric:var(--tnum)}
.chart-legend{display:flex;justify-content:center;gap:24px;margin-top:16px}
.chart-legend-item{display:flex;align-items:center;gap:6px;font-size:var(--fs-small);color:var(--t2)}
.chart-legend-dot{width:10px;height:10px;border-radius:var(--rd-0)}
.chart-legend-dot.red{background:var(--acc)}
.chart-legend-dot.green{background:rgba(var(--acc-rgb),.45);box-shadow:inset 0 0 0 1px var(--acc)}
/* Two math cards side by side */
@media(max-width:1024px){.calc-grid{grid-template-columns:1fr}}
/* The plot is one value at every width and the chart scrolls sideways on a narrow
   card. It must not shrink instead: at a ~104px plot the smallest bar is 9.8px tall
   and both its figures are unreadable. */
@media(max-width:768px){.rc-res{grid-template-columns:1fr}.rc-res.tri{grid-template-columns:1fr}}

/* ── THE CHART BELOW 480px ───────────────────────────────────────────────────
   The groups shrink here instead of scrolling. A flex-shrink:0 basis wide enough
   to hold a $271,250 total sets a min-content floor five groups deep, which at
   380px is 110px wider than the chart's own 274px content box. The three things
   that set that floor are re-measured for the width actually available:
     available 380 - 48 container - 36 padding - 2 border = 294px
     gap 16 -> 8, so 4 gaps cost 32px and 262px is left for five groups: 52.4px
   Against that 52.4px, measured in Google Sans Flex with tabular figures:
     .stacked-total "$271,250"  13px -> 62.5px   OVER    10px -> 47.6px  fits
     .stacked-label "$3.5M rev" 12px -> 61.9px   OVER    10px -> 51.6px  fits
   The label also stops being nowrap-bound at this size: it already carries a <br>,
   and its second line breaks again at its own space if it has to, so "$3.5M rev"
   becomes "$3.5M" over "rev" rather than forcing the column wider. .stacked-bar's
   44px min-width goes to 0 for the same reason — it was protecting a label that is
   now sized to fit.
   Every figure and every label is still present and still legible; nothing is
   hidden, abbreviated or scrolled. */
@media(max-width:480px){
  .roi-cc{padding:18px}
  .stacked-chart{gap:8px;overflow-x:hidden}
  .stacked-group{flex:1 1 0;min-width:0}
  .stacked-bar{min-width:0}
  .stacked-total{font-size:var(--fs-mono-xs)}
  .stacked-label{font-size:var(--fs-mono-xs)}
  /* The segment figures come down with them. At 320px — narrower than anything
     this chunk is asked to verify, but a real device — the column is 40.4px and
     "$157.5K" sets 46px at 12px and 38.3px at 10px, so 12px clipped and 10px does
     not. It also buys back plot headroom rather than costing it: the smallest
     segment is 4.19% of the 352px plot and needs 1.2 line-heights, which is 14.4px
     at 12px and 12px at 10px. */
  .stacked-seg{font-size:var(--fs-mono-xs)}
}

/* ── THE BAR FIGURES: .roi-bv ────────────────────────────────────────────────
   THE FIGURE DOES NOT GO BACK INSIDE ITS BAR. It sat there once, right-aligned
   against the fill, which works only while the bar is wider than the label — and
   the first bar is 10% of the track. Measured, "325 hrs" needs 86px against a 95px
   bar at 1440 and a 78px bar at 1023; at 380 the bar is 14px and "1,300 hrs" needs
   103px against 55px. The cliff is at ~1080px, not at the 1024 breakpoint.

   One treatment at all widths rather than inside-the-bar above 1200px and outside
   below it, for three reasons. The element has to be a sibling of .roi-bco to sit
   in the row's own grid at narrow widths, and a sibling cannot also be a child of
   .roi-bar; faking the inside position with absolute offsets would need the bar's
   inline percentage in CSS, which it is not. A single treatment also means one
   layout to verify instead of a mode change at a breakpoint. And it retires the
   --bg-on---acc pairing the old inside-the-bar label depended on: the figure is now
   --t1 on the page background, which measures far above AA at every width instead
   of 4.57 at one of them.

   .roi-bar keeps its class, its inline width, its background, its transition and
   its .anim toggle. The chart-animation observer, which does
   querySelectorAll('.ann-b,.stacked-bar,.roi-bar'), is untouched — it is the empty
   fill that animates now, and the figure beside it no longer rides the animation.

     >= 768px   name | track | figure, one row. The track gives up the width the
                figure column takes: at 1023px that leaves 664px of track and a
                66px first bar with nothing to hold.
     <  768px   a single row cannot hold all three: at 380px that is 118 + 103 +
                32 of gaps against 274px, leaving 21px of track. So the row becomes
                two — name and figure at opposite ends of the first line, track full
                width below. The ordinary mobile bar-chart form; holds to 320px. */
.roi-bv{font-family:var(--fm);font-weight:var(--fw-mono-em);font-size:var(--fs-mono);
  font-variant-numeric:var(--tnum);line-height:var(--lh-mono);color:var(--t1);
  white-space:nowrap;text-align:right;min-width:8ch}
@media(max-width:767px){
  .roi-bi{display:grid;grid-template-columns:1fr auto;grid-template-areas:'name value' 'track track';gap:6px 12px;align-items:center}
  .roi-bll{grid-area:name;min-width:0;text-align:left}
  .roi-bco{grid-area:track}
  .roi-bv{grid-area:value;min-width:0}
}

/* AI
   One column, not two. The right-hand track held the decorative orb and nothing else, so
   with the orb removed a 1fr 1fr grid would leave half the section empty. The four
   capability rows take the freed width as a 2x2 grid instead of a 1x4 stack, which keeps
   each row's reading measure at roughly the 536px it had before rather than stretching a
   14px bullet list across 1152px. Collapses to one column below 768px. */
.ai-s .container{display:block}
.ai-fs{display:grid;grid-template-columns:1fr 1fr;gap:28px}
.ai-f{display:flex;gap:20px;padding:24px;border-radius:var(--rd);border:1px solid transparent;transition:all .35s;cursor:default}
.ai-f:hover{background:var(--s2);border-color:var(--br-hi)}
.ai-fi{flex-shrink:0;width:52px;height:52px;border-radius:var(--rd);display:flex;align-items:center;justify-content:center;font-size:var(--fs-glyph)}
.ai-fi.red{background:rgba(var(--acc-rgb),.15)}.ai-fi.dk{background:rgba(var(--acc-rgb),.15)}.ai-fi.grn{background:rgba(var(--paper-rgb),.06)}.ai-fi.org{background:rgba(var(--paper-rgb),.06)}
.ai-f h4{font-family:var(--ff);font-weight:var(--fw-sub);font-size:var(--fs-h4);letter-spacing:var(--tr-h3);line-height:var(--lh-h3);margin-bottom:8px}

/* FEATURES */
/* FEATURES - asymmetric editorial grid
   A 12-column grid with no card fills. Row 1 is the three cells carrying a product
   screenshot - one wide landscape at span 6, two portrait phones at span 3 - and the
   remaining six sit below as hairline-separated rows, two columns of three. 6+3+3 = 12
   and 6x2 = the six, so no cell is empty; keep it that way when adding a feature.
   The three image cells lead in the DOM as well as visually, so reading order and
   visual order agree. Nothing here is repositioned out of source order. */
.fg{display:grid;grid-template-columns:repeat(12,1fr);gap:0 32px;margin-top:60px}
.fc{background:none;border:0;border-radius:0;padding:0;position:relative;grid-column:span 6;border-top:1px solid var(--br);padding-top:28px;margin-top:28px}
.fc.fc-w{grid-column:span 6}
.fc.fc-t{grid-column:span 3}
/* Row 1 carries the images and opens the section, so it takes no top hairline: the
   hairlines exist to separate the six text rows from each other. */
.fc.fc-w,.fc.fc-t{border-top:0;padding-top:0;margin-top:0}
.fi{width:48px;height:48px;border-radius:var(--rd);display:flex;align-items:center;justify-content:center;font-size:var(--fs-glyph);margin-bottom:20px}
.fc h3{font-family:var(--ff);font-size:var(--fs-h3);font-weight:var(--fw-sub);letter-spacing:var(--tr-h3);line-height:var(--lh-h3);margin-bottom:12px}
.fc p{font-size:var(--fs-body);color:var(--t2);line-height:var(--lh-body)}
/* PRODUCT SCREENSHOTS (chunk 10C)
   Every img carries its intrinsic width and height as attributes, and the rules below
   restate the same ratio as an explicit aspect-ratio. Either alone would reserve the box
   before the bytes arrive; both are present because .tall sizes by height rather than by
   width, and the attribute-derived ratio is the only thing that can resolve that box
   early. Measured cumulative layout shift with the images in place is 0.000.
   All five are below the fold at every tested width, so all five are lazy.
   No pill, tag or label is overlaid on any image - the caption sits below it. */
/* In the three image-led feature cells the figure is the FIRST child, so all three
   images start on the same line and the row reads as a band rather than as three
   pictures at three different heights. The margin only applies where a figure follows
   copy, which is the AI section. */
.fc-fig{margin:0 0 24px}
/* ── ROW 1 IS TOP ALIGNED ────────────────────────────────────────────────────
   The three image cells stretch to the row's height, and the earlier version of
   this rule spent that slack as an auto top margin on .fi — which bought a second
   aligned edge at the bottom by opening a hole at the TOP. At 1440px it measured
   234.4px in the wide cell and 29.7px in the shorter tall cell: the reader met
   the void before the content, which is the wrong end of the cell to lose.

   Matching bottom edges was never a requirement. Every cell now runs figure →
   caption → icon → headline → body at the same 24px figure-to-icon step, so
   every cell starts at the row's top edge and the shorter one simply ends
   earlier, with the section's own divider beneath it. Nothing is stretched to
   close the difference.

   Growing the image to eat the slack was tested and rejected: .fc-fig.wide img
   is already width:100% of a 560px column, so gaining 234.4px of height means
   919.7px of width at the capture's own 1800x1173 ratio — 64% wider than the
   column. The only way to fill is to crop the screenshot, and a cropped product
   capture is a worse trade than a ragged bottom edge.

   The six text rows below were already top aligned and are unchanged. */
.fc-w,.fc-t{align-self:stretch;display:flex;flex-direction:column}
/* The icon block sits under the image in these cells rather than opening them,
   so it keeps the 4px of optical padding and takes no margin: the figure's own
   24px bottom margin is the whole of the step, in all three cells. */
.fc-w .fi,.fc-t .fi{margin-top:0;padding-top:4px}
.fc-fig img{display:block;border:1px solid var(--br-hi);border-radius:var(--rd-0);background:var(--bg)}
.fc-fig.wide img{width:100%;height:auto;aspect-ratio:1800/1173}
/* Portrait phone captures are 889x1800. Sized by height so a 2.02:1 tall image cannot
   drive the cell to 700px+ when the column is wide. */
.fc-fig.tall img{height:420px;width:auto;max-width:100%;aspect-ratio:889/1800;margin:0 auto}
.fc-cap{font-size:var(--fs-small);color:var(--t3);line-height:var(--lh-body);margin-top:10px}
.fc-fig.tall .fc-cap,.ai-fig .fc-cap{text-align:center}
.ai-fig{margin:20px 0 0}
.ai-fig img{display:block;height:380px;width:auto;max-width:100%;aspect-ratio:889/1800;margin:0 auto;border:1px solid var(--br-hi);border-radius:var(--rd-0);background:var(--bg)}

/* .shot: landscape product captures.
   These two are light-mode screenshots of the app and they are NOT recoloured —
   a product screenshot is documentary, and tinting it to fit a dark page would
   make it a picture of software that does not exist. What stops a white panel
   reading as glare on a #0a0a0a page is the frame around it, not a filter on it,
   so the capture is seated in a recessed well: a --s1 mat with real padding, a
   hairline --br edge, and more space above it than the phone figures get. Read
   as an inset panel the page is holding up, the white stops being a light source
   and becomes a surface.
   Sizing is restrained on purpose: the well is the column's full width and no
   more, so at 1440px the capture renders 351px wide inside a 373px column.
   aspect-ratio is released back to auto so the intrinsic ratio from each img's
   own width/height attributes reserves the box — the two captures are 828x706
   and 952x682, so one shared ratio would be wrong for both and would shift. */
.ai-fig.shot{margin:26px 0 0;padding:10px;background:var(--s1);border:1px solid var(--br);border-radius:var(--rd)}
.ai-fig.shot img{width:100%;height:auto;aspect-ratio:auto;border-color:var(--br);border-radius:var(--rd-0);margin:0}
.ai-fig.shot .fc-cap{margin:12px 2px 2px}
/* Below 900px the text column inside .ai-f is only ~210px, and a 828px-wide
   desktop capture shown at 210px is a thumbnail, not a screenshot. The well is
   pulled back across the icon gutter (52px icon + 20px gap) so it uses the full
   width the card has, which is a 1.35x gain on every glyph in it. The phone
   captures are unaffected: they are portrait and already fit. */
@media(max-width:900px){.ai-fig.shot{margin-left:-72px}}

/* HOW IT WORKS */
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:40px;margin-top:60px;position:relative}
.step{text-align:center;position:relative;z-index:1}
.step h3{font-family:var(--ff);font-size:var(--fs-h3);font-weight:var(--fw-sub);letter-spacing:var(--tr-h3);line-height:var(--lh-h3);margin-bottom:12px}
.step p{font-size:var(--fs-body);color:var(--t2);max-width:280px;margin:0 auto;line-height:var(--lh-body)}

/* INDUSTRIES - hairline tag row
   A list of names, not a grid of content. A wrapped flex row, not a grid, and
   deliberately: a grid of eleven items leaves four empty cells in the last row at
   1440px, against the no-empty-tiles rule. A flex row has exactly as many items as
   there is content for at every width, by construction. */
.ig{display:flex;flex-wrap:wrap;justify-content:center;gap:12px;margin-top:60px}
.ic{display:inline-flex;align-items:center;gap:10px;background:none;border:1px solid var(--br);border-radius:var(--rd-pill);padding:11px 20px;text-align:left;transition:border-color .35s,color .35s;cursor:default}
.ic:hover{border-color:var(--acc)}
.ie{font-size:var(--fs-glyph);margin-bottom:0;display:inline-flex;align-items:center;color:var(--t2)}
/* Industry names are labels in a five-across grid, not subsection headings — the
   small step, so they do not wrap to three lines in a 186px column. */
.ic h4{font-family:var(--ff);font-size:var(--fs-small);font-weight:var(--fw-ui);line-height:var(--lh-h3)}

/* TESTIMONIALS */
.test-s{border-top:1px solid var(--br);border-bottom:1px solid var(--br)}
.tg{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-top:60px}
.tc{background:var(--s2);border:1px solid var(--br);border-radius:var(--rd);padding:36px;transition:all .3s}
.tc:hover{border-color:var(--br-hi);transform:translateY(-2px)}
/* Five inline icons rather than five characters, so the 2px letter-spacing that
   used to separate the stars becomes a flex gap. Container alignment only. */
.ts{color:var(--t1);font-size:var(--fs-h4);display:flex;gap:4px;margin-bottom:20px}
/* The page's only italic, and it is body type, not display type, so the
   descender-clipping risk the brief warns about does not arise here: the line
   box grows from 27.4px to 29.7px and no glyph reaches past it. */
.tc blockquote{font-size:var(--fs-body);color:var(--t2);line-height:var(--lh-body);margin-bottom:24px;font-style:italic}
.ta{display:flex;align-items:center;gap:14px}
.tav{width:44px;height:44px;border-radius:50%;object-fit:cover;border:2px solid var(--br-hi)}
.tn{font-family:var(--ff);font-weight:var(--fw-ui);font-size:var(--fs-body)}
.tr{font-size:var(--fs-small);color:var(--t3)}

/* PRICING */
.pg{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-top:60px;align-items:start}
.pc{background:var(--s2);border:1px solid var(--br);border-radius:var(--rd);padding:44px 36px;position:relative;transition:all .3s}
/* Featured tier: the accent border and the scale step mark it out. The
   accent-to-surface wash it used to carry is removed with the other two. */
.pc.ft{border-color:var(--acc);background:var(--s2);transform:scale(1.04)}
/* 11px label on a solid --acc fill inside a pill that cannot grow: near-black,
   for the same 4.57-vs-4.15 reason as the chart labels. */
.pc.ft::before{content:'MOST POPULAR';position:absolute;top:-12px;left:50%;transform:translateX(-50%);background:var(--acc);color:var(--bg);font-family:var(--fm);font-size:var(--fs-mono-sm);font-weight:var(--fw-mono-em);letter-spacing:var(--tr-mono);padding:6px 18px;border-radius:var(--rd-pill);line-height:var(--lh-mono)}
.pn{font-family:var(--ff);font-size:var(--fs-h4);font-weight:var(--fw-sub);letter-spacing:var(--tr-h3);line-height:var(--lh-h3);margin-bottom:8px}
.pd{font-size:var(--fs-small);color:var(--t3);margin-bottom:24px}
.pp{display:flex;align-items:baseline;gap:4px;margin-bottom:4px}
/* The price is a large metric, so the whole group — currency mark, figure and
   period — is mono, and reads as one unit instead of three fonts in a row. */
.pcu{font-family:var(--fm);font-size:var(--fs-metric);font-weight:var(--fw-metric-xl);letter-spacing:var(--tr-metric-xl);color:var(--t2);font-variant-numeric:var(--tnum)}
.pam{font-family:var(--fm);font-size:var(--fs-metric-lg);font-weight:var(--fw-metric-xl);letter-spacing:var(--tr-metric-xl);line-height:1;font-variant-numeric:var(--tnum)}
.ppe{font-family:var(--fm);font-size:var(--fs-mono);font-weight:var(--fw-metric);color:var(--t3);letter-spacing:var(--tr-mono);font-variant-numeric:var(--tnum)}
.pf{list-style:none;margin-bottom:32px}
/* A spec list, not prose: it stays on the small step so a ten-item list does not
   make the three cards twice as tall as the section around them. */
.pf li{padding:11px 0;font-size:var(--fs-small);color:var(--t2);display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--br);line-height:var(--lh-h3)}
.pf li:last-child{border:none}
.pf .ck{color:var(--t1);font-weight:var(--fw-mono-em);flex-shrink:0}
.pc .btn{width:100%;justify-content:center}

/* TEAM / ABOUT
   No rules of its own: a headline and a paragraph, which .stitle and .ssub
   already style. The section keeps #team — it is a frozen anchor and the footer
   links to it. */

/* CTA */
.cta{padding:120px 0;text-align:center;position:relative;overflow:hidden}
.cta .container{position:relative;z-index:1}
.cta .stitle{max-width:700px;margin:0 auto 20px}
.cta .ssub{margin:0 auto 40px;text-align:center}
.cta-a{display:flex;justify-content:center;gap:16px;flex-wrap:wrap}

/* FOOTER */
.footer{border-top:1px solid var(--br);padding:80px 0 40px}
.ft-g{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;gap:60px;margin-bottom:60px}
/* The footer and the legal lines stay on the small step. Body at 18px in a
   four-column footer would out-shout the sections above it. */
.ft-br p{font-size:var(--fs-small);color:var(--t2);line-height:var(--lh-body);margin:16px 0 24px;max-width:320px}
.ft-so{display:flex;gap:12px}
.ft-s{width:40px;height:40px;border-radius:var(--rd);background:var(--s2);border:1px solid var(--br);display:flex;align-items:center;justify-content:center;color:var(--t2);text-decoration:none;font-size:var(--fs-glyph-sm);transition:all .3s}
.ft-s:hover{border-color:var(--acc);color:var(--acc);background:var(--acc-lo)}
/* Footer column headings are small uppercase labels, so mono. */
.ft-c h4{font-family:var(--fm);font-weight:var(--fw-mono-lb);font-size:var(--fs-mono);letter-spacing:var(--tr-mono);text-transform:uppercase;color:var(--t1);margin-bottom:20px;line-height:var(--lh-mono)}
.ft-c ul{list-style:none}.ft-c li{margin-bottom:12px}
.ft-c a{color:var(--t2);text-decoration:none;font-size:var(--fs-small);transition:color .3s}
.ft-c a:hover{color:var(--acc)}
.ft-b{border-top:1px solid var(--br);padding-top:30px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:16px}
.ft-b p{font-size:var(--fs-small);color:var(--t3)}
.ft-bl{display:flex;gap:24px}
.ft-bl a{font-size:var(--fs-small);color:var(--t3);text-decoration:none;transition:color .3s}
.ft-bl a:hover{color:var(--t2)}

/* BULLET LISTS IN CARDS */
/* One .ai-f carries a lead sentence above its bullets (chunk 14E1, the claim
   rehomed out of the hero). It sits in the bullets' register, not the body
   register: at --fs-body it would be 18px above 14px list items in a 373px
   column and would read as the card's real content with the capabilities as
   a footnote, which is backwards. */
.ai-f p{font-size:var(--fs-small);color:var(--t2);line-height:var(--lh-body);margin-bottom:10px}
.ai-f ul{list-style:none;padding:0;margin:0}
.ai-f li{position:relative;padding-left:20px;font-size:var(--fs-small);color:var(--t2);line-height:var(--lh-body);margin-bottom:6px}
.ai-f li::before{content:'';position:absolute;left:0;top:.42em;width:var(--fs-glyph-sm);height:var(--fs-glyph-sm);background-color:var(--acc);-webkit-mask:var(--ic-arrow-right) center/contain no-repeat;mask:var(--ic-arrow-right) center/contain no-repeat}
.ssub ul{list-style:none;padding:0;margin:12px 0 0}
.ssub li{position:relative;padding-left:20px;margin-bottom:6px}
/* .ssub must sit on a WRAPPER holding the <p> and the <ul> as siblings, not on the
   <p> itself. A <ul> inside a <p> closes the <p> early, the <li> elements stop being
   .ssub descendants, and both selectors below silently match nothing. */
.ssub li::before{content:'';position:absolute;left:0;top:.42em;width:.8em;height:.8em;background-color:var(--t1);-webkit-mask:var(--ic-check) center/contain no-repeat;mask:var(--ic-check) center/contain no-repeat}

/* ELEVATED PLAN */
.plan-s{padding:100px 0;position:relative}
/* THE PLAN - hairline rungs
   Not three cards side by side: the copy is a sequence (set up, then run, then win
   work) and three peer cards say "pick one". Order is carried by reading order and by
   the hairline above each rung.

   NO STAIRCASE. Indenting each rung further right than the last, with the right edge
   pinned, gives the three body columns three different measures — measured at 1440,
   697.3 / 661.9 / 626.5px, a 70.8px spread inside one section — and three left edges
   where the eye expects one. Inconsistent measure within a section is a typographic
   fault on its own terms, and with the right edge fixed a staircase and a
   misalignment are the same picture. All three rungs are flush left.

   STILL NOT THE SAME FAMILY as .cost-s below it, which was the staircase's other
   job. .cost-sheet is an ORDERED, INDEXED list: a content-sized first track
   holding a 13px 01-04 index, a rule under each row, and the whole thing set in
   a 7fr/4fr outer grid beside a phone capture. This is a full-bleed two-column
   editorial split with no index, a 7fr headline track carrying 30px h3s, and the
   rule above each row rather than below. A numbered spec table beside a figure
   and a full-width headline/body split are not one family; see the section
   census in the chunk 14 report, which puts the longest run of any family at 2.

   .plan-s keeps its class - chunk 9's constellation measures it. */
.plan-s .steps{display:block;margin-top:60px;position:relative}
.plan-s .step{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,12fr);gap:8px 48px;background:none;border:0;border-top:1px solid var(--br);border-radius:0;padding:36px 0 0;margin-top:36px;text-align:left;position:relative;transition:none}
.plan-s .step h3{font-family:var(--ff);font-size:var(--fs-h3);font-weight:var(--fw-sub);letter-spacing:var(--tr-h3);line-height:var(--lh-h3);margin-bottom:0}
.plan-s .step p{font-size:var(--fs-body);color:var(--t2);line-height:var(--lh-body);max-width:none;margin:0}

/* IDENTITY TRANSFORMATION */
.id-trans{display:flex;gap:40px;margin:48px auto 0;max-width:700px;align-items:center;justify-content:center}
.id-from,.id-to{flex:1;padding:24px;border-radius:var(--rd);text-align:center}
.id-from{background:color-mix(in srgb,var(--t3) 10%,transparent);border:1px solid var(--br)}
.id-to{background:var(--acc-lo);border:1px solid rgba(var(--acc-rgb),.2)}
.id-arrow{font-size:var(--fs-glyph-xl);color:var(--acc);flex-shrink:0}
.id-label{font-family:var(--fm);font-size:var(--fs-mono-sm);font-weight:var(--fw-mono-lb);letter-spacing:var(--tr-mono);text-transform:uppercase;margin-bottom:8px;line-height:var(--lh-mono)}
.id-from .id-label{color:var(--t3)}
/* 12px --acc on the --acc-lo wash measured 4.10 — the reading chunk 13
   reported as 4.11 and could not close without this token. */
.id-to .id-label{color:var(--acc-tx)}
.id-text{font-family:var(--ff);font-size:var(--fs-h4);font-weight:var(--fw-sub);letter-spacing:var(--tr-h3);line-height:var(--lh-h3)}

/* ── THE $127K, EXPLAINED ────────────────────────────────────────────────────
   A DATASHEET, and deliberately not any of the four families already on the page.
   .plan-s directly above is an indented staircase and .roi-s directly below is a
   narrative column beside a chart, so a third card grid here would have put three
   consecutive sections in one family. An indexed spec table is the one industrial
   form the page had not used, it is what "engineering datasheet" in the direction
   actually looks like, and it carries four items of unequal length without pretending
   they are four peer options to choose between.

   Transparent, per direction 4.3: the constellation's ambient field has to cross this
   section. Structure is the hairline at the section's top edge, the rule under each
   row and the rule above the reconciliation. The only filled surface is the tinted
   half of the comparison, which is a 15% wash rather than a panel.

   No eyebrow. The page is at the four-eyebrow cap already (plan, ROI, features,
   pricing), so this section leads on its headline. */
.cost-s{border-top:1px solid var(--br)}
/* 7fr/4fr, not 1fr/1fr. The four descriptions run 26 to 34 words and want a real
   measure; the phone is a 889x1800 portrait that is already tall enough at 300px and
   gets absurd wider. */
.cost-body{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,4fr);gap:64px;align-items:start;margin-top:56px}
.cost-sheet{list-style:none;margin:0;padding:0;border-top:1px solid var(--br-hi)}
/* align-items:baseline, so the index sits on the same baseline as the heading beside
   it rather than on the same top edge. At 13px against a 23px heading, top alignment
   left the number floating a third of a line high and the column read crooked. */
.cost-row{display:grid;grid-template-columns:auto minmax(0,1fr);gap:0 32px;align-items:baseline;padding:26px 0;border-bottom:1px solid var(--br)}
/* The index is the one place on this page a figure is decorative rather than measured,
   and it is still a figure: tabular so 01 through 04 hold one column width. */
.cost-ix{font-family:var(--fm);font-size:var(--fs-mono);font-weight:var(--fw-mono-lb);letter-spacing:var(--tr-mono);line-height:var(--lh-mono);color:var(--acc);font-variant-numeric:var(--tnum)}
.cost-row h3{font-family:var(--ff);font-size:var(--fs-h4);font-weight:var(--fw-sub);letter-spacing:var(--tr-h3);line-height:var(--lh-h3);margin-bottom:8px}
.cost-row p{font-size:var(--fs-body);color:var(--t2);line-height:var(--lh-body)}
/* Framing, not recolouring, per direction 8.1. The product's own surfaces carry a blue
   cast this page no longer does, and the screenshot is documentary evidence, so the
   mismatch is bounded by a hairline and a --bg mat instead of being processed away.
   Same treatment as .fc-fig img and .ai-fig img, which is the point: one frame. */
.cost-fig{margin:0}
.cost-fig img{display:block;width:100%;max-width:300px;height:auto;aspect-ratio:889/1800;margin:0 auto;border:1px solid var(--br-hi);border-radius:var(--rd-0);background:var(--bg)}
.cost-recon{margin-top:64px;border-top:1px solid var(--br-hi);padding-top:40px}
.cost-rh{font-family:var(--ff);font-size:var(--fs-h3);font-weight:var(--fw-sub);letter-spacing:var(--tr-h3);line-height:var(--lh-h3);margin-bottom:28px}
.cost-cmp{display:grid;grid-template-columns:1fr 1fr;gap:24px}
/* Hairline-bounded blocks, so --rd-0 and no elevation. The tint on the second is the
   visual variation a two-cell grid owes under direction 6.3, and it is the same
   --acc-lo wash .id-to already uses rather than a new value. */
.cost-c{padding:26px 28px;border:1px solid var(--br);border-radius:var(--rd-0)}
.cost-c-a{background:var(--acc-lo);border-color:rgba(var(--acc-rgb),.2)}
.cost-v{font-family:var(--fm);font-size:var(--fs-metric-lg);font-weight:var(--fw-metric-xl);letter-spacing:var(--tr-metric-xl);line-height:1;color:var(--t1);font-variant-numeric:var(--tnum)}
/* --t2, not --t3, and the same value in BOTH cells. MEASURED, not chosen: --t3 clears AA
   against --bg at 4.61, which is the whole margin it has, so on the --acc-lo wash of the
   tinted cell it drops to 4.15 and fails 4.5 for 13px text. Sampled from the rendered
   pixels, not computed from the tokens: composited, the wash is rgb(42,16,15).
     --t3 #7a7a7a on the wash   4.15  FAIL
     --acc #e43531 on the wash  4.11  FAIL   (the .id-to precedent, checked and rejected)
     --t2 #9a9a9a on the wash   6.33  PASS   (7.04 on --bg in the plain cell)
   One value across both cells rather than a per-cell override, so there is no second
   label colour to keep in sync. The label still reads as a label against the body copy
   beside it: 13px against 18px, uppercase against sentence case, 600 against 400, and
   .14em of tracking. Case, size, weight and tracking carry it; colour was never what
   separated the two. */
.cost-l{font-family:var(--fm);font-size:var(--fs-mono);font-weight:var(--fw-mono-lb);letter-spacing:var(--tr-mono);text-transform:uppercase;color:var(--t2);margin:12px 0 14px;line-height:var(--lh-mono)}
.cost-c p{font-size:var(--fs-body);color:var(--t2);line-height:var(--lh-body)}
.cost-close{margin-top:28px;font-size:var(--fs-body);color:var(--t2);line-height:var(--lh-body);max-width:640px}

/* RESPONSIVE */
@media(max-width:1024px){
/* Single column, and the row gap stays 0 so the headline keeps its own 24px margin as
   the only space between it and the body copy — a 40px row gap on top of that margin
   read as a 64px hole. The .hero-v rule that took the 40px explicitly went with the
   hero screenshot; the column gap is retained because it is the two-track value the
   rule above 1024px inherits from. */
.hero .container{grid-template-columns:1fr;gap:0 40px}
.roi-s > .container:first-child{grid-template-columns:1fr;gap:48px}
/* Features drop from 12 tracks to 6: the wide image cell still spans the row, the two
   portrait cells sit side by side under it, and the six text rows pair up two across. */
.fg{grid-template-columns:repeat(6,1fr);gap:0 28px}
.fc{grid-column:span 3}.fc.fc-w{grid-column:span 6}.fc.fc-t{grid-column:span 3}
.fc.fc-t{border-top:0;padding-top:0;margin-top:28px}
.ft-g{grid-template-columns:repeat(2,1fr);gap:40px}
.plan-s .step{grid-template-columns:minmax(0,8fr) minmax(0,12fr);gap:8px 32px}
/* CTA labels are 19px now and the nav's width budget no longer covers a 240px
   logo, five links, a login link and two buttons. The secondary "Contact Us"
   drops out here rather than at 768 — the mobile menu still carries it — and the
   link gap tightens, which leaves both remaining labels on one line.
   Tier CTAs get the same treatment from the other direction: the card and the
   button each give back horizontal padding so the frozen 23-character label
   fits a 309px card on one line. */
.nav-links{gap:20px}.nav-cta .desktop-contact{display:none}
.pc{padding:36px 20px}.pc .btn{padding-left:16px;padding-right:16px}
/* The datasheet drops the phone beside it and runs full width. The screenshot is not
   hidden. It moves under the table, capped narrower, so it still lands as evidence
   without taking a third of a 976px container to do it. */
.cost-body{grid-template-columns:1fr;gap:44px}
.cost-fig img{max-width:264px}
}
@media(max-width:768px){
.section{padding:80px 0}.nav-links{display:none}.nav-tog{display:block}
/* Every recomposed layout collapses to one column below 768px.
   Features: one track, so the wide cell, the two portrait cells and the six text rows
   all stack; each text row keeps its hairline as the separator it already is. */
.fg{grid-template-columns:1fr;gap:0}
.fc,.fc.fc-w,.fc.fc-t{grid-column:1/-1}
.fc.fc-t{border-top:0;padding-top:0;margin-top:32px}
.steps{grid-template-columns:1fr;gap:48px}
/* The two-track rung collapses to one so the heading sits above its body rather
   than beside it. The indent-reset that used to live here is gone with the
   staircase itself (14B2) — there is nothing left to reset. */
.plan-s .steps{grid-template-columns:1fr}
.plan-s .step{grid-template-columns:1fr;gap:10px;padding:28px 0 0;margin-top:28px}
/* The four AI capability rows go back to a single column here, matching every other
   multi-column block at this breakpoint. */
.ai-fs{grid-template-columns:1fr}

.tg{grid-template-columns:1fr}
.pg{grid-template-columns:1fr;max-width:420px;margin-left:auto;margin-right:auto}.pc.ft{transform:scale(1)}
.ft-g{grid-template-columns:1fr;gap:32px}
/* Padding narrows on mobile; the label does not. Dropping it below --fs-cta
   would take the --white-on---acc primary CTA back under the large-text
   threshold, which is the whole reason the step exists. */
.tb-row{flex-wrap:wrap;gap:16px}.hero-acts{flex-direction:column;align-items:stretch;max-width:340px}.hero-acts .btn{text-align:center;justify-content:center;padding:14px 20px}
.id-trans{flex-direction:column;gap:20px}.id-arrow{transform:rotate(90deg)}
.rc-res{grid-template-columns:1fr}.ann-c{height:160px}
.nav-logo-full{display:none}.nav-logo img.nav-logo-mark{display:inline-block;height:44px;width:auto}
/* The index moves above its heading rather than beside it. Keeping the two-track row
   at this width spends 45px of a 332px content column on a 13px number and squeezes
   the description into a ragged 287px measure; the hairline under each row is already
   doing the separating the column was doing.
   The comparison stacks for the same reason: two 152px cells cannot hold a 48px figure
   and 30 words each. */
.cost-row{grid-template-columns:1fr;gap:6px;padding:22px 0}
.cost-cmp{grid-template-columns:1fr;gap:16px}
.cost-body{margin-top:40px}.cost-recon{margin-top:48px}
}
@media(max-width:480px){.ig{grid-template-columns:1fr 1fr}}

.reveal{opacity:1;transform:translateY(0);transition:all .8s cubic-bezier(.4,0,.2,1)}
.reveal.hidden{opacity:0;transform:translateY(30px)}
.reveal.visible{opacity:1;transform:translateY(0)}
.mob-m{position:fixed;inset:0;background:var(--bg);background:color-mix(in srgb,var(--bg) 98%,transparent);backdrop-filter:blur(20px);z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;opacity:0;pointer-events:none;transition:opacity .3s;padding:20px 24px}
.mob-m.active{opacity:1;pointer-events:all}
.mob-m a{font-family:var(--ff);font-size:var(--fs-body);font-weight:var(--fw-ui);color:var(--t1);text-decoration:none;transition:color .3s;padding:8px 20px}
.mob-m a:hover{color:var(--acc)}
/* .mob-m a and .ft-c a both out-specify .btn and .btn-p (0,1,1 against 0,1,0), so
   without these two rules the mobile-menu and footer primary CTAs lose both the
   size and the colour those classes are supposed to give them. Colour is the
   serious half: .ft-c a{color:var(--t2)} was painting the footer's "Free Trial"
   label mid-grey on the accent fill at 1.72 — well under any threshold — and it
   had been doing so before this chunk. Both labels are --white at --fs-cta now,
   which is 4.15 against --acc and passes at the 3:1 large-text threshold. */
.mob-m .btn{margin-top:6px;font-size:var(--fs-cta);font-weight:var(--fw-cta)}
.mob-m .btn-p,.ft-c .btn-p{color:var(--white)}
.ft-c .btn{font-size:var(--fs-cta);font-weight:var(--fw-cta)}
/* A <button> does not inherit body's font-family, so this glyph was rendering in
   the UA default (Arial) — the one thing on the page still set outside the two
   families. */
.mob-x{position:absolute;top:16px;right:16px;background:none;border:none;color:var(--t1);font-family:var(--ff);font-size:var(--fs-glyph-close);cursor:pointer;z-index:10000;width:48px;height:48px;display:flex;align-items:center;justify-content:center}

/* REDUCED MOTION
   Covers every animation and transition in this file, including hover and
   entry transitions. Animations are collapsed to their end state rather than
   cancelled: a negative delay with a 1ms duration lands on the final keyframe
   immediately, so nothing is left invisible or stuck part-way.
   .reveal is pinned visible because the page's JavaScript adds .hidden
   (opacity:0) to every reveal element on load and only removes it when the
   IntersectionObserver fires. */
@media(prefers-reduced-motion:reduce){
*,*::before,*::after{animation-delay:-1ms!important;animation-duration:1ms!important;animation-iteration-count:1!important;transition-delay:0s!important;transition-duration:1ms!important;scroll-behavior:auto!important}
html{scroll-behavior:auto!important}
.reveal,.reveal.hidden,.reveal.visible{opacity:1!important;transform:none!important}
}

