/* ==========================================================================
   Homepage 4.0 — scoped styles (template-homepage-v4.php)

   The visual language is lifted from template-homepage-rb.php / the
   #hero, #challenge-transform, #clients, #deliver, #expertise and #workshop
   blocks in assets/modern/styles.css. Tokens, .la-container, .btn-*,
   .wave-top and .wave-bottom all come from styles.css — nothing is
   redefined here. Everything below is namespaced .hv4-* so it cannot leak.

   Background images are reused from the existing psd_assets library:
     hero            psd_assets/Updated-Header-2.1.webp  (Header Photo.webp on mobile)
     light sections  psd_assets/noise-tile.png
     correction      psd_assets/facades/Why it Works Mountain.webp
     why-liminalarc  ../images/rebrand-images/About-Us/mesh-v3.png

   Eight sections. Background rhythm:
     hero navy → problem light → cost light → conditions navy (waved)
     → correction paper → delivery paper → why navy (waved at the top)
     → cta blue

   The last of those is the shared #cta-learning component. Its structure lives
   in styles.css and it sits outside the .hv4 wrapper; this file recolours it
   (section 10) and nothing more, and only on this template.
   ========================================================================== */

/* ==========================================================================
   1. Shared section shell
   ========================================================================== */

.hv4 h1,
.hv4 h2,
.hv4 h3,
.hv4 h4 {
    font-family: "leo", sans-serif;
    font-weight: 500
}

.hv4 p {
    text-wrap: pretty
}

.hv4-section {
    padding: 6rem 0;
    position: relative
}

.hv4-section--light {
    background: linear-gradient(rgba(251, 251, 251, 0.2), rgba(251, 251, 251, 0.2)), url("psd_assets/noise-tile.png") repeat, var(--light-gray)
}

/* .hv4-section--white — a flat `background: var(--white)` — was deleted 09-18
   as part of the consistency audit. It had no users left: --paper below is the
   white this page actually runs, and every section that was ever --white took
   the textured one when it arrived.

   *** IT CAME BACK 09-22, AS A PLAIN RULE RATHER THAN A MODIFIER. *** See THE
   TWO FLAT WHITE SECTIONS further down. It has exactly two users and they are
   named there with the reasons; a modifier class would invite a third, and a
   third would leave the page with no dominant ground at all. */

/* WHITE WITH THE NOISE TILE, added 09-17: "let's make how to get production and
   one slice ninety days sections both on the white background with texture."
   #hv4-correction was --teal and #hv4-delivery was --navy.

   Same three-layer construction as --light, one step brighter: a 20% white wash
   over the tile over white, rather than over --light-gray. The wash is what
   keeps the grain from reading as dirt on a pure white ground.

   Distinct from --white, which is flat and is what the cards inside these
   sections use. A textured section with flat white cards on it is the page's
   existing figure-on-ground relationship; a flat white section with flat white
   cards has none.

   #hv4-why .wave-top is in this rule because a stretched viewBox cannot carry
   a texture — see the note on the #f4f6f6 rule further down, which is the same
   problem solved the same way. Its path was inverted in the markup on the same
   pass.

   *** #hv4-conditions .wave-bottom USED TO BE IN THIS RULE AND IS NOT ANY
   MORE, 09-22. *** It went with #hv4-correction to flat white. A wave takes the
   ground of the section it OPENS ONTO, and those two waves now open onto
   different grounds. They are not a matched pair and must not be reunited. */
.hv4-section--paper,
#hv4-why .wave-top {
    background: linear-gradient(rgba(255, 255, 255, .2), rgba(255, 255, 255, .2)), url("psd_assets/noise-tile.png") repeat, var(--white)
}

/* #hv4-why's wave is MASKED rather than filled, 09-17: "the texture needs to
   wrap in the wave." Its <path> is deleted from the markup; the same curve is
   the mask below, cut to the shape ABOVE it.

   This is the inversion rule taken one step further, and it is the better shape
   of the answer. The rule above exists because a stretched viewBox smears a
   fill pattern, so the textured half has to come from the element's background.
   A mask separates the two completely: the MASK stretches (a wave is meant to)
   while the background it reveals does not, and the unmasked half is transparent
   rather than a flat colour — so #hv4-why's mesh artwork, which is inset:0 over
   the whole section, now runs up through the wave band instead of stopping dead
   at a 70px rectangle of flat teal.

   The path is the complement of the one that was in the markup: the same curve,
   closed UPWARD to y=0 instead of down to y=95. If the wave shape is ever
   changed, both this and #hv4-conditions' fill have to change together — they
   are drawn to be the same curve. */
#hv4-why .wave-top {
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 90' preserveAspectRatio='none'><path d='M0,0 L1440,0 L1440,50 C1260,80 1080,80 900,55 C720,30 540,15 360,22 C180,32 90,48 0,55 Z' fill='white'/></svg>") no-repeat 0 0/100% 100%;
    mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 90' preserveAspectRatio='none'><path d='M0,0 L1440,0 L1440,50 C1260,80 1080,80 900,55 C720,30 540,15 360,22 C180,32 90,48 0,55 Z' fill='white'/></svg>") no-repeat 0 0/100% 100%
}

/* The green band's closing curve is NOT here, and it is the one wave on the page
   with no background of its own. It lives over #cta-learning — see section 10.

   Worth keeping the dead end visible, because it is the obvious thing to try
   again: for one afternoon this was `#hv4-why .wave-bottom { background:
   --dark-navy }`, following the inversion rule above. That rule assumes the
   neighbour is ONE colour. The CTA is navy on the left and a photograph on the
   right, so no value works, and the one that looked closest — the navy — is
   exactly what stranded a band between the curve and the photo. */

.hv4-section--navy {
    background: var(--dark-navy)
}

.hv4-section--teal {
    background: var(--dark-teal)
}

/* .hv4-section--green is GONE. #hv4-why used it for part of one day and now
   takes .hv4-section--navy like every other dark section.

   Kept as a note because the green is the kind of thing that gets re-proposed:
   it was asked for ("the we built these conditions section please change to our
   dark green 259271"), built, and then reversed the same day ("let's also make
   the why liminalarc section navy"). It was also the page's one unfixable
   contrast problem — #259271 is mid-luminance, so white is 3.87:1 on it and
   --dark-navy 3.95:1, and NOTHING clears AA on it either way. Two columns of
   17px body copy sat on that. Navy carries white at 15.3:1, so the reversal
   closed the largest accessibility gap on the page as a side effect. */

/* Headline + lead — matches #clients / #deliver / #expertise scale.

   *** THIS CLAMP IS THE SITE-WIDE SECTION-HEADLINE SCALE. DO NOT TUNE IT HERE. ***
   `clamp(1.5rem, 2.9vw, 2.7rem)` is written verbatim six times in
   assets/modern/styles.css — .challenge-section h2, .transform-section h2,
   #clients h2, #deliver h2, #expertise h2, #workshop h2 — and styles.css is what
   liminalarc.co runs today. Jess, 09-25: "that liminalarc.co is our standard...
   I need this page to be consistent with the existing site."

   It was briefly 2rem here, earlier the same day, to fix a real inversion: at 768
   the section h2 measured 24px while .hv4-aside-title INSIDE it measured 28px, so
   "Capabilities Are the Unit of Decision" outsized "How to Get Production to
   Match the Pilot" directly below it. At 992 it was 28.8 against 28.0, a tie,
   which is the same fault one pixel short of visible.

   *** THE INVERSION WAS REAL BUT THE FIX WAS AT THE WRONG END. *** Raising the
   section headline pulled this page off the site scale to accommodate a
   subheading that was itself oversized. The standard's own answer is
   .deliver-row h3 — clamp(1.5rem, 2.5vw, 2rem), a full 0.125rem below its
   section h2 at the top of the range — so .hv4-aside-title was brought onto THAT
   and this returned to the standard. See .hv4-aside-title's note.

   *** 10-01: THE TWO-CLAMP PATTERN WAS REPLACED WITH A SINGLE SMOOTH RAMP AND
   THAT WAS REVERTED THE SAME DAY. DO NOT REBUILD IT. ***

   The problem is real and is still here. The standard runs TWO clamps per
   headline — this one above 768 and clamp(1.6rem, 6.8vw, 2.3rem) below it
   (styles.css, six times: 1219, 1235, 1406, 1602, 1821, 1936). A bare vw term is
   a line through the ORIGIN, so two of them with different slopes can only meet
   there, and these do not. Measured across the rung: h2 36.8 -> 24, hero h1
   43.2 -> 32. The page gets visibly SMALLER as the window gets WIDER.

   The fix works, technically. A calc() with a rem intercept lets ONE expression
   pass through two chosen points, so clamp(1.5rem, calc(1.244rem + 1.517vw),
   2.7rem) hits the standard's own 25.6 at 375 and 41.76 at 1440 and removes the
   step. It was built, measured at seven widths and shipped for about an hour.

   *** IT WAS REVERTED BECAUSE OF WHAT IT DID IN THE MIDDLE, WHICH NOBODY ASKED
   ABOUT. *** A line with a positive intercept sits ABOVE an origin line
   everywhere below their shared point — that is not a tuning artefact, it is
   arithmetic, and it is unavoidable in this technique. Every section headline on
   the page grew on laptops: 28.8 -> 35.0 at 992, 29.7 -> 35.4 at 1024. The hero
   went 38.7 -> 47.7 at 992. Phone and 1440 were unchanged, which is exactly why
   it read as safe and why the verification sweep at those two widths said so.

   Jess, 10-01, on being shown the laptop figures: "put the jump back."

   ⚠️ SO THE CHOICE HERE IS ALREADY MADE AND IT IS NOT THE OBVIOUS ONE. The page
   matching the rest of the site at every width beat the page resizing smoothly.
   The 767/768 step is INHERITED and is not an hv4 bug — if it is ever worth
   fixing, fix it in styles.css so every page gets it. Rebuilding the ramp here
   trades a step nobody has complained about for a size change on the commonest
   screens, which is the trade that was just refused.

   ⚠️ AND THE LESSON IS BIGGER THAN THIS RULE. Anchoring at 375 and 1440 and
   verifying at 375 and 1440 proves only that the anchors hold. The change lived
   entirely in the widths that were not checked. Check the MIDDLE of a range after
   changing anything expressed as a ramp. */
.hv4 h2 {
    font-size: clamp(1.5rem, 2.9vw, 2.7rem);
    line-height: 1.2;
    color: var(--dark-navy);
    margin: 0 0 1.25rem;
    max-width: 20em
}

/* 66.67%, copied off the original homepage rather than derived. This element is
   the v4 equivalent of `.challenge-intro` on liminalarc.co — same 1.1rem, same
   1.6 line-height, same --text-muted — and that rule caps at `max-width: 66.67%`
   (styles.css). Asked for by name: "on the original homepage, I feel like the
   text under the headline went further across the page like 2/3. let's match
   that." So this is the same number, not an approximation of it.

   It has been 68ch (641px, 55.5%), then 73ch (689px, 59.7%) reaching for Tim's
   9/2 note ("I feel like it should be like 60%, not 50, 40%"), and is now 770px
   at 1440. A percentage keeps widening on a large monitor where a ch cap stops,
   which is a real cost — at the 1720px container ceiling this runs about 87
   characters, past the ~75 where a reader starts losing the head of the next
   line. That is the tradeoff of matching the original exactly, and matching it
   is the instruction. Do not "fix" it back to a ch cap. */
.hv4-lead {
    font-size: 1.1rem;
    line-height: 1.6;
    color: var(--text-muted);
    max-width: 66.67%;
    margin: 0 0 1.5rem
}

.hv4-body p {
    font-size: 1.05rem;
    line-height: 1.7;
    color: var(--text-muted);
    max-width: min(68ch, 100%);
    margin: 0 0 1.2rem
}

.hv4-body p:last-child {
    margin-bottom: 0
}

.hv4-body strong {
    color: var(--dark-navy);
    font-weight: 500
}

/* On the dark sections. Two grounds now, not three — the --green variants that
   sat alongside these were dropped when #hv4-why went navy, along with the
   stronger tints it needed (its ground was light enough that .78 and .85 read as
   faded on it; on navy they do not). */
.hv4-section--navy h2,
.hv4-section--teal h2,
.hv4-section--navy h3,
.hv4-section--teal h3 {
    color: var(--white)
}

.hv4-section--navy .hv4-lead,
.hv4-section--teal .hv4-lead {
    color: hsla(0, 0%, 100%, .85)
}

.hv4-section--navy .hv4-body p,
.hv4-section--teal .hv4-body p {
    color: hsla(0, 0%, 100%, .78)
}

.hv4-section--navy .hv4-body strong,
.hv4-section--teal .hv4-body strong {
    color: var(--white)
}

/* ==========================================================================
   2. Hero — reuses psd_assets/Updated-Header-2.1.webp and the #hero
      image-placement math, so the artwork lands identically to the RB home.
   ========================================================================== */

#hv4-hero {
    background: var(--dark-navy);
    min-height: min(50vw + 80px, 670px);
    max-height: 670px;
    display: flex;
    align-items: center;
    padding: 5rem 0 4rem;
    position: relative;
    overflow: hidden
}

/* BIGGER, 09-18: "I'd like this to scale more to fill the space. just make sure
   the right edge of the photo stays aligned to the edge of the grid. that can't
   move." 1.22 -> 1.37 and the cap 960 -> 1080, which is the same 1.125x on both
   so the crossover between them does not move: the cap starts governing at a
   content width of 788px either way (960/1.22 = 787, 1080/1.37 = 788). Change one
   without the other and the hero jumps size at some middle viewport.

   THE RIGHT EDGE LOOKS AFTER ITSELF AND THAT IS THE WHOLE DESIGN OF THIS RULE —
   do not add a correction for it. The .webp carries transparent padding down its
   right side worth 9% of its width, and the `- .09 * var(--hero-image-w)` term in
   background-position is proportional, not a fixed pixel nudge. So at any scale
   the VISIBLE photo edge lands at exactly clamp(1.5rem, 9.9vw, 9rem) from the
   container edge, which is .la-container's own padding — the 12th column. Checked
   at 1080: image right edge 1394.64, minus 97.2 of transparency, = 1297.44, and
   the container's content right edge is 1440 - 142.56 = 1297.44.

   THE VERTICAL TERM IS A CENTRE PLUS A TASTE VALUE, AND BOTH HALVES WERE
   ESTABLISHED BY BRACKETING, 09-18. The request was "I'd like for this photo to
   feel middle aligned in the negative navy space." Three candidates were built
   and two were rejected, which is what fixes the third:

     - Centre in the 670px band -> 113px of navy above the panel, 113 below.
       REJECTED: "this went higher instead of lower."
     - Centre in the navy BELOW .la-header (the 130px the nav covers, so the
       open navy runs 130..670) -> 178 above, 48 below, i.e. 48/48 of *visible*
       navy. REJECTED: "now photo too low."
     - Band centre + 2rem -> 145 above, 82 below. That is within a pixel of the
       midpoint of the two rejections (146/81), which is the only thing either
       rejection actually tells you.

   SO THERE IS NO DERIVABLE CENTRE HERE AND THE FILE SHOULD STOP LOOKING FOR
   ONE. Both centres are real, both were computed correctly, and both were wrong
   on screen. The +2rem is taste, in the same spirit as the +1.5rem it replaces
   ("the image is too high let's push it down some"), and it is written as a
   SEPARATE TERM so it can be retuned without touching the two derived ones.

   THE NAV-AWARE VERSION IS ALSO GONE ON ITS OWN MERITS: it needed .la-header's
   height as a hard-coded 130px, and .la-header is 80px at 1024, which left 9px
   under the photo there. Every term in this rule is now proportional again.

     - The .webp is 1955x1160 and the framed mountain panel inside it runs
       y=188..990, so the PANEL's centre is y=589 against the FILE's y=580. The
       artwork carries 9 source px more emptiness above the panel than below —
       plus a fully transparent top 140px, which is where most of it lives.
     - Centre the file and the panel lands 9 * (image-h/1160) low. So the term
       backs off by exactly that: .00776 is 9/1160.
     - Written as a fraction of --hero-image-h rather than a flat pixel value so
       it tracks the artwork at every scale, the same way the horizontal term
       does. A literal 5px would be right only at the 1080 cap.

   Measured at 1440, panel against the 670px band: 145px above, 82px below. The
   rejected states, same measurement: 113/113 (band centre, too high) and 178/48
   (visible-navy centre, too low). 142/84 was the earlier +1.5rem.

   THE +2rem IS THE ONLY TUNABLE NUMBER IN THIS RULE. It retunes the earlier
   +1.5rem ("the image is too high let's push it down some"). DO NOT EDIT THE
   .00776 to move the picture — that is a property of the FILE, wrong only if the
   artwork is recut, and folding taste into it means the next person cannot tell
   which half is measurement. Past about 2.5rem in either direction the horizon
   leaves the frame, so the tuning range is roughly 0 to 2.5rem.

   THE TWO POSITION TERMS ARE NOT SYMMETRIC AND SHOULD NOT BE MADE SO. The
   horizontal one is load-bearing — it is what holds the right edge on the grid,
   and it has no taste in it at all. The vertical one is two derived lengths plus
   a chosen one.

   *** THE MASK IS NOT SYMMETRIC EITHER, AND THAT IS THE POINT. *** It was
   `transparent 0%, #000 10%, #000 90%, transparent 100%` — a matched 10% fade
   on both sides. 09-25: "add a little more of a soft gradient on the mountain on
   desktop for the header. it feels a tad harsh."

   The two sides are not the same thing. The RIGHT 10% fades the far edge of the
   photo panel's surround and is fine as it is. The LEFT is where the wireframe
   ridge starts, and at 10% it went from nothing to full strength inside 144px at
   1440 — the mesh arrived as an edge rather than emerging out of the navy.

   The left ramp now runs 0 → 38% with two stops inside it (.35 at 14%, .78 at
   26%) rather than one linear run. The mid stops are what makes it read as soft:
   a plain two-stop gradient over the same distance still crosses its halfway
   opacity at the halfway point, and it was the middle of the ramp that looked
   hard, not its ends. 38% at 1440 is 547px, which lands the mesh at full
   strength just before the ridge peaks.

   *** SUPERSEDED — AND THE RAMP ABOVE NEVER FADED THE MESH IN AT ALL. *** 10-22,
   the same edge a second time: "can you improve this fading on the home page a
   little more too? feels just a little too abrupt."

   ⚠️⚠️ THE RAMP WAS MEASURED FROM THE VIEWPORT AND THE ARTWORK DOES NOT START
   AT THE VIEWPORT. The picture is anchored RIGHT and is 1080 wide, so at 1440
   its left edge stands at 314.64px — 21.85% in. The ramp's stops are 0 / 201.6
   / 374.4 / 547.2, so:

       314.6px of the ramp   falls on EMPTY NAVY, fading nothing
       the artwork's FIRST COLUMN renders at alpha 0.631
       232.6px of the ramp   is all that ever lands on the mesh

   So there was no fade-in. There was a 63% STEP at the artwork's edge followed
   by the last third of a gradient — which is exactly what "abrupt" names, and
   why 09-25's careful three-stop shaping did not fix it: the shape was right and
   it was being applied to the wrong 38% of the band.

   ⚠️ THE FIX IS THE ANCHOR, NOT THE SHAPE. The ramp now STARTS at
   `--hero-image-l`, the artwork's own left edge, so the mesh emerges out of the
   navy from zero. The two interior stops keep their proportions inside the ramp
   (.35 at 36.8% of it, .78 at 68.4% — 14/38 and 26/38), so the softness she
   approved in 09-25 is preserved exactly; it is just applied to the part of the
   band that has artwork in it.

   ⚠️⚠️ AND THE END IS A SHARE OF THE IMAGE NOW, NOT A VIEWPORT PERCENTAGE,
   WHICH IS BOTH THE EXTRA SOFTNESS SHE ASKED FOR AND A BUG FIX. "Just before the
   ridge peaks" is a fact about the FILE, and a viewport % cannot hold against
   it: the image is anchored right, so `--hero-image-l` climbs with the viewport
   while 38% climbs faster, and the ramp SHORTENS as the screen widens — 232px at
   1440, 36px at 1920, and it INVERTS past ~2240 where 38% falls left of the
   artwork. Pinned to the artwork it is one length at every width.

   .35 is read off the file. Per-column alpha-weighted mean luminance of
   Updated-Header-2.1.webp (1955x1160): 0.2–1.5 through x 0–400, 1.5–2.1 to 600,
   2.4–3.0 to 690, then it breaks upward at x 704 (5.85) and plateaus 3.5–6.8
   until the opaque photo panel starts at x 1192. So the mesh turns from "faint"
   to "strong" at x≈690 = .353 of the file, and the ramp ends there: full
   strength immediately before the ridge, which is what the old note WANTED. At
   the 1080 cap that is 378px of fade against today's effective 232.6, and the
   artwork starts at 0 instead of 0.631.

   ⚠️ `--hero-image-r` IS FACTORED OUT AND `background-position` NOW READS IT.
   It is the same expression that was already inline there; the mask needs it to
   locate the left edge, and a second hand-copy of it is precisely how the mask
   and the picture would drift apart again. The computed `background-position` is
   unchanged — verify it reads `calc(100% - 45.36px) 41.5515px` at 1440.

   ONLY THE LEFT HALF MOVED. The 90%/100% pair is untouched, and it must stay
   untouched — see the right-edge note above, which is about the same edge of the
   same artwork. If the mesh ever needs softening at its BOTTOM instead, that is
   a second mask layer and needs `mask-composite: intersect`, not more stops
   here; multiple mask layers default to union and would make it MORE opaque. */
#hv4-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    --la-content-width: calc(min(100vw, 1720px) - 2 * clamp(1.5rem, 9.9vw, 9rem));
    --hero-image-w: min(calc(var(--la-content-width) * 1.37), 1080px);
    --hero-image-h: calc(var(--hero-image-w) / 1.685);
    --hero-image-r: calc(max((100vw - 1720px)/2, 0px) + clamp(1.5rem, 9.9vw, 9rem) - .09*var(--hero-image-w));
    --hero-image-l: calc(100% - var(--hero-image-r) - var(--hero-image-w));
    --hero-fade: calc(.35 * var(--hero-image-w));
    background-image: url("psd_assets/Updated-Header-2.1.webp");
    background-repeat: no-repeat;
    background-size: var(--hero-image-w) var(--hero-image-h);
    background-position: right var(--hero-image-r) top calc((min(50vw + 80px, 670px) - var(--hero-image-h))/2 - .00776*var(--hero-image-h) + 2rem);
    -webkit-mask-image: linear-gradient(to right, transparent var(--hero-image-l), rgba(0, 0, 0, .35) calc(var(--hero-image-l) + .368 * var(--hero-fade)), rgba(0, 0, 0, .78) calc(var(--hero-image-l) + .684 * var(--hero-fade)), #000 calc(var(--hero-image-l) + var(--hero-fade)), #000 90%, transparent 100%);
    mask-image: linear-gradient(to right, transparent var(--hero-image-l), rgba(0, 0, 0, .35) calc(var(--hero-image-l) + .368 * var(--hero-fade)), rgba(0, 0, 0, .78) calc(var(--hero-image-l) + .684 * var(--hero-fade)), #000 calc(var(--hero-image-l) + var(--hero-fade)), #000 90%, transparent 100%);
    pointer-events: none;
    z-index: 0
}

#hv4-hero > * {
    position: relative;
    z-index: 1
}

/* The offer-first headline is longer than the RB hero's, so it runs a notch
   smaller and a touch wider to hold three lines inside the 670px band.

   *** 10-01: ONE CONTINUOUS CLAMP FROM 320 TO 1440, REPLACING THE TWO-CLAMP
   LADDER. THIS PAGE NOW DIVERGES FROM styles.css ON PURPOSE. ***

   The standard splits every headline into a phone clamp and a desktop clamp at
   767, and the two do not meet. Measured on this page before the change:

                          767      768     delta
     #hv4-hero h1         43.2     32      -11.2
     .hv4 h2              36.8     24      -12.8

   A headline that loses a THIRD of its size when the window gets one pixel
   WIDER. liminalarc.co does exactly the same thing — phone
   `clamp(1.6rem, 6.8vw, 2.3rem)` against desktop `clamp(1.5rem, 2.9vw, 2.7rem)`,
   styles.css:1219/559 — so this was inherited faithfully and was not a bug on
   this page. It was put to Jess on 10-01 as a straight choice between matching
   the site and not having the jump, with the site-wide fix offered as the third
   option: "smooth it on this page only."

   ⚠️ SO THE RULE "liminalarc.co IS OUR STANDARD" NOW HAS ONE NAMED EXCEPTION,
   AND IT IS THIS. Do not "restore consistency" by copying the standard's two
   clamps back — that is the thing that was rejected. If the standard is ever
   fixed site-wide, this page should go back to inheriting it.

   ⚠️ 10-01: A SINGLE calc() RAMP WAS BUILT TO CLOSE THAT STEP AND WAS REVERTED
   THE SAME DAY. clamp(2rem, calc(1.806rem + 1.893vw), 3.7rem) passes through 36
   at 375 and 56.16 at 1440 and removes the cliff. It also raised the hero from
   38.7 to 47.7 at 992 — a ramp with an intercept runs high between its anchors —
   and that is what got it reverted. The full account is on .hv4 h2. This rule is
   back on the value it had before, and the raise now lives in a ≤991 block only.

   *** THE CLOSING CTA IS WHY THE PHONE AND TABLET SIZES MOVED, AND IT IS A
   SHARED COMPONENT. *** `.cta-learning-text h2` (styles.css:3652/3702/3723) is
   the LAST headline on the page and it was bigger than the FIRST one. Measured
   against this rule's values:

                 closing CTA   hero, before   result
       375          33.6           30.8       CTA 9% bigger
       768          38.4           32         CTA 20% bigger   <- the worst case
       1024         41.6           39.9       4%, a tie
       1366         54.4           53.3       2%, a tie
       1440         54.4           56.2       hero wins

   Jess, 10-01: raise the hero, do not shrink the CTA — that block closes every
   practice-area page, so moving it moves the whole site.

   ⚠️ THE FIX IS SCOPED TO ≤991 AND THE LAPTOP TIES ARE DELIBERATELY LEFT ALONE.
   Two pixels on a 50px headline cannot be seen, and closing them would require
   raising the hero at 1440 — the one width everybody agrees is already right.
   I first reported this inversion as "every screen under 992", having only
   checked phone and tablet; it actually runs to ~1300. ⚠️ MEASURE THE WHOLE
   RANGE BEFORE DESCRIBING A PROBLEM'S EXTENT — the wrong extent led straight to
   the wrong fix, and the page-wide smoothing above is what came of it.

   The cap stays 3.7rem rather than styles.css's 4.2rem because the headline is
   longer than the RB hero's and has to hold its lines inside the 670px band. */
#hv4-hero h1 {
    font-size: clamp(2rem, 3.9vw, 3.7rem);
    line-height: 1.1;
    color: var(--white);
    max-width: 58%;
    margin: 0 0 1.5rem
}

/* --bright-yellow on the headline's third line, was --bright-green.
   8.61:1 on the --dark-navy ground against the green's 9.41:1 — both far past
   what a 59px heading needs (3:1), so this is a pure preference call. */
#hv4-hero h1 em {
    font-style: italic;
    color: var(--bright-yellow)
}

.hv4-hero-lead {
    font-size: 1.1rem;
    line-height: 1.6;
    color: var(--white);
    max-width: 52%;
    margin: 0 0 2rem
}

/* The hero button hovers to a DARKER GREEN, 09-18: "this should go 259271 on
   hover." #259271 is --dark-green; the resting state is .btn-teal's own
   --light-green #4abd92, so this is the same hue one step down.

   It replaces styles.css's `.btn-teal:hover { opacity: .85 }`, which is why the
   opacity has to be reset here as well as the background set — a .85 button on a
   navy hero goes MUDDIER, not darker, because the navy shows through it. Naming
   the colour is the whole point of the instruction.

   Scoped to .hv4 deliberately. .btn-teal is a site-wide component and every other
   page still fades on hover; this is a Homepage 4 decision, not a component
   change. If it is ever meant to be site-wide it belongs in styles.css:402 and
   this rule should be deleted rather than left to shadow it.

   Contrast: white on #259271 is 3.51:1, down from 3.04:1 on #4abd92. The label is
   11.5px bold brandon caps, which is normal text, so neither state clears 4.5:1
   — but the hover state is the BETTER of the two and does not make anything
   worse. That shortfall belongs to .btn-teal site-wide and is not opened here. */
.hv4 .btn-teal {
    transition: background-color .2s
}

.hv4 .btn-teal:hover {
    background: var(--dark-green);
    opacity: 1
}

@media(max-width: 991px) {
    #hv4-hero {
        min-height: 620px;
        padding: 3rem 0 4rem;
        isolation: isolate
    }

    #hv4-hero h1,
    .hv4-hero-lead {
        max-width: 100%
    }

    /* THE HERO IS RAISED ON PHONE AND TABLET ONLY, AND THAT BOUNDARY IS THE
       WHOLE POINT OF THE RULE. 10-01, "keep the hero raise on phone", then
       "yes, do that" on the tablet half.

       The problem it fixes is that the page's LAST headline outsized its
       FIRST. .cta-learning-text h2 in styles.css is a shared block that
       closes every practice-area page, and Jess's instruction was to raise
       the hero rather than shrink it. Measured, CTA against the base hero:

         375    33.6 vs 30.8    CTA 9% bigger
         768    38.4 vs 32      CTA 20% bigger   <- the worst case
         991    41.6 vs 38.6    CTA 8% bigger

       Where the numbers come from:
         9.6vw   = 36px at 375. Swept — 4 lines, longest 313px of a 330px
                   column. 38px gives a short third line, so 36 is inside
                   the band and 38 is the edge.
         2.25rem = 36px, so the floor holds that size below 375 and the
                   hero never drops back under the CTA's own 33.6 floor.
         2.7rem  = 43.2px, reached at 450 and held to 991. That beats the
                   CTA at every width in this band.

       *** THE BASE RULE TAKES OVER AT 992 AT 38.7px, A 10% STEP DOWN, AND
       THAT STEP IS ACCEPTED, NOT AN OVERSIGHT. *** Smoothing it was built
       on 10-01 and reverted the same day — see the long note on .hv4 h2.
       A single ramp that removes this step also raises every section
       headline on laptops, which is what got it reverted. Do not "fix" the
       step by widening this rule to all widths.

       *** RE-RUN THE 375 LINE SWEEP IF THE HEADLINE COPY CHANGES. *** Read
       line boxes with Range.getClientRects, not element height. */
    #hv4-hero h1 {
        font-size: clamp(2.25rem, 9.6vw, 2.7rem)
    }

    .hv4-hero-lead {
        max-width: 46ch
    }

    #hv4-hero::before {
        background: url("psd_assets/Header%20Photo.webp") 60% 65%/auto 125% no-repeat;
        -webkit-mask-image: none;
        mask-image: none
    }

    #hv4-hero::after {
        content: "";
        position: absolute;
        inset: 0;
        background: linear-gradient(90deg, rgba(6, 41, 53, 0.55) 0%, rgba(6, 41, 53, 0.15) 50%, rgba(6, 41, 53, 0) 80%), linear-gradient(180deg, rgb(6, 41, 53) 0%, rgb(6, 41, 53) 4%, rgba(6, 41, 53, 0.55) 40%, rgba(6, 41, 53, 0.78) 75%, rgba(6, 41, 53, 0.98) 100%), linear-gradient(rgba(108, 224, 181, 0.06) 1px, transparent 1px) 24px 24px/48px 48px, linear-gradient(90deg, rgba(108, 224, 181, 0.06) 1px, transparent 1px) 24px 24px/48px 48px;
        pointer-events: none;
        z-index: 0
    }
}

@media(max-width: 767px) {
    /* *** THE HEADER DOES OVERLAY THE HERO ON PHONES. THIS NOTE USED TO SAY THE
       OPPOSITE AND THE CHECK IT RECOMMENDED IS THE REASON. ***

       09-30: "this is crashing into each other", a phone screenshot of the logo
       sitting on top of "AI Transformation,". It was, and 09-25 had already
       reported it as "this feels a little too close to the top" and set 5.75rem
       to clear it. That value was then reverted by a pass that measured
       `#hv4-hero.getBoundingClientRect().top` and read 56 — concluding the hero
       started 56px below a header in normal flow.

       ⚠️ THAT NUMBER WAS THE PAGE'S SCROLL POSITION, NOT THE HEADER. The check
       only reports what it claims when scrollY is 0, and it says nothing about
       the header in any case — an absolutely positioned header sits AT the
       hero's own top, so both boxes start at the same y and a rect on the hero
       alone can never see the overlap. Read both, and read the visible mark
       rather than the header box:

           const b = document.querySelector('#hv4-hero').getBoundingClientRect().top;
           document.querySelector('.la-mobile-logo img').getBoundingClientRect().bottom - b;

       Measured at 375, relative to the hero's top: .la-header is
       `position: absolute; z-index: 2000`, 0..80. The logo mark is 23..58, the
       hamburger 31..49. The h1's first line box began at 54 under 3.5rem — four
       pixels ABOVE the bottom of the logo. So the padding is clearance after
       all, and the earlier pass's "breathing room, not clearance" is exactly
       the sentence that let the collision back in.

       5.75rem = 92px puts the h1's first line at 90 and leaves 32px under the
       logo, which is the same clearance the tablet block gets from its 3rem
       over a header that is not overlapping there. It is 09-25's own value,
       restored, now with a measurement behind it rather than against it.

       It is NOT the standard's 2.5rem, and the standard is not applicable here:
       styles.css's #hero keeps `min-height: 620px` with `align-items:
       flex-start`, so its content is pushed down by the flex box regardless of
       the padding. This hero drops min-height at ≤767 (see below), so its
       padding is the only thing holding the headline off the logo.

       The cost is 36px of hero height — 506px becomes 542px. With the
       announcement bar above it the hero still ends around y=586 on an 812px
       fold, so the lead and the button both stay above it. */
    /* min-height comes off on a phone. The ≤991 rule sets 620px, and at 375 the
       headline, lead, chips and button stack to 394px — so 226px of nothing was
       being manufactured and then split 113/113 by `align-items: center`. The
       bottom half of that is what got flagged; it was never a spacing decision,
       just the leftover of a tablet minimum applied to a much narrower column.

       Without it the hero is content plus padding, 506px, and the 3.5rem above
       is doing the job it is described as doing rather than being one share of
       a remainder. 620px stays at 768-991, where the content really is short
       enough to need a floor. */
    #hv4-hero {
        min-height: 0;
        padding: 5.75rem 0 3.5rem
    }

    /* *** THE HERO FONT-SIZE FOR PHONES IS NOT IN THIS BLOCK — IT IS IN THE
       ≤991 BLOCK ABOVE, AND ONE RULE DELIBERATELY COVERS BOTH BANDS. *** The
       derivation, the sweep and the hero/CTA table are all on that rule. Do
       not add a second hero size here; it would silently split a decision
       that was taken for phone and tablet together.

       What used to be here was clamp(1.75rem, 8.2vw, 2.7rem) — 30.75px at
       375. That was a reduction, not a copy of the site standard, and it was
       the smaller of the two sizes the hero/CTA inversion was about.

       *** THE SWEEP THAT PRODUCED 8.2vw WAS RE-RUN AND ITS NUMBERS NO LONGER
       REPRODUCE. *** It found 36–32px giving FIVE lines at 375 with the bare
       word "AI" stranded on line one, which is why the rule existed. The
       headline copy changed after that sweep was taken, and the old comment
       said in capitals to re-run it if that ever happened. Measured live at
       375 on the current copy:

         36px   4 lines, longest 313px against a 330px column   <- live
         38px   4 lines, but 325/318/225/315 — a short third line

       The old note's reasoning still holds in general — Chromium stops
       balancing past about four lines and falls back to greedy wrapping, so
       the failure is a cliff and not a gradient — only its numbers were
       about different words. */
    .hv4-hero-lead {
        font-size: 1rem
    }
}

/* ==========================================================================
   4. Readiness gap — the problem section's graphic. Design option 8A,
      "The Bar", from the 09-18 handoff titled "Design simplification for
      executives".

      Two 240px circles either side of a horizontal orange dimension bar. The
      left circle is drawn closed, the right one open, and the bar between
      them carries the label and the closing statement. The section is named
      after a distance and this is the first version where the distance is an
      object on the page rather than an implication.

      It replaced option 22A — two white panels of four items a side with a
      SCOPE / DATA / OWNERSHIP / ATTENTION channel between them — which
      replaced 20A, which said the same thing in stacked full-width rows.
      *** The four pairs are deliberately gone and the reasons are on the
      .hv4-rg block below. Read that before restoring them. ***
   ========================================================================== */

/* A darker ground than the shared --light-gray (#fbfbfb) this modifier
   normally paints. It is the handoff's own section ground, and the graphic
   still needs it: the hatch is ink laid straight on the section with no frame
   around it, and the one white card has to separate from that ground by its
   own material. Against #fbfbfb the card's border is a 1% step and only the
   shadow holds it. #f4f6f6 is still almost white but far enough off that the
   card reads as a card. The noise tile stays, so the section keeps the page's
   surface.

   *** #hv4-problem WAS IN THIS RULE AND IS NOT ANY MORE, 09-22. *** The two
   were deliberately given one ground so the pair read as a single field. That
   is the decision the flat white reverses — see below. The paragraph that used
   to stand here explaining why they shared a ground is kept there, because the
   reasoning is still the reason the white had to go on #hv4-problem rather
   than on #hv4-cost.

   The wave that carries #hv4-cost into the navy band is in this rule too, and
   that is the point of it: see below. It opens onto #hv4-cost, which did not
   change, so it stays here. */
#hv4-cost,
#hv4-conditions .wave-top {
    background: linear-gradient(rgba(244, 246, 246, .2), rgba(244, 246, 246, .2)), url("psd_assets/noise-tile.png") repeat, #f4f6f6
}

/* ==========================================================================
   THE TWO FLAT WHITE SECTIONS — ONE SIDE OF EACH ONE-FIELD PAIR
   Tried 09-22, kept the same day: "let's fold in the white section break for
   now. I think I like it."

   "the feedback from my boss was that he didn't love the transition from one
   section to the next on the white noise bkgs since two of them have two
   sections. So I want to see how it would look if one of the sections was just
   white."

   This REVERSES the decision the two rules above were written to carry out.
   Both pairs were deliberately put on ONE ground so the 176px between them
   read as space inside a single field rather than a boundary; the complaint is
   that the boundary therefore is not legible at all. Making the upper section
   of each pair flat white gives the seam a real tonal step, so the change of
   subject is visible without a rule.

   *** THE SPACING WENT BACK TO NORMAL WHEN THIS LANDED. *** Three things had
   already been tried against the same complaint — a 1px hairline, then 112,
   144 and 176px of space — and none of them worked, because the problem was
   never the size of the gap. With the grounds doing the separating, all four
   shortened-seam overrides came off and .hv4-section's 6rem a side runs at
   every join. *** IF THIS IS EVER REVERTED, THE SEAMS HAVE TO BE RECONSIDERED
   TOGETHER WITH IT *** — the ledger is on the #hv4-cost comment.

   WHICH SECTION OF EACH PAIR GOES WHITE IS NOT ARBITRARY:
   - #hv4-problem, not #hv4-cost. #hv4-cost holds .hv4-spend, a WHITE CARD
     (measured rgb(255,255,255)). On a white ground that card stops existing,
     and the card is what makes the chart read as a titled figure instead of
     loose bars. The ground under it has to stay off-white.
   - #hv4-correction, not #hv4-delivery. #hv4-delivery's wave carries into
     #hv4-why, and #hv4-why .wave-top is in the --paper rule above — the wave
     and the section it runs out of must stay byte-identical or the seam the
     whole rule exists to prevent comes straight back.

   The noise tile comes off with the colour. It cannot stay: the tile is the
   page's surface on every OTHER light ground, so a flat white section is also
   the only smooth one, which is a second signal saying the same thing.

   *** THE WAVE ABOVE #hv4-correction HAS TO MOVE WITH IT. *** This is the
   third selector below, and leaving it out is exactly the bug this stylesheet
   was built to avoid. A wave element takes the ground of the section it OPENS
   ONTO — its SVG path fills the navy, the CSS background is the far side — so
   #hv4-conditions .wave-bottom was byte-identical to .hv4-section--paper
   because #hv4-correction used to be --paper. The moment #hv4-correction went
   flat white, the wave was still textured white and a visible band appeared
   across the full width where the curve ended. Reported on sight: "make sure
   the wave has the white."

   *** #hv4-why .wave-top IS CORRECT AS IT IS. DO NOT TOUCH IT. *** It opens
   onto #hv4-delivery, which is still --paper, so it must STAY in the --paper
   rule. The two waves are not a matched pair here; each one matches the
   section it runs into, and those two sections are now different.

   *** TO REVERT: #hv4-problem goes back into the #f4f6f6 rule, #hv4-correction
   and #hv4-conditions .wave-bottom go back into --paper, and this rule is
   deleted. Three selectors, and then read the seam note above. ***
   ========================================================================== */
#hv4-problem,
#hv4-correction,
#hv4-conditions .wave-bottom {
    background: var(--white)
}

/* Why the wave is in that rule, and why its path is inverted in the markup.

   The wave used to be a light shape painted over the navy: `fill="#f4f6f6"`,
   the same hex as the section above it. That produced a visible horizontal seam
   at the top of the wave — the band the user reported — because the section
   above is not #f4f6f6. It is a noise tile over #f4f6f6 with a 20% wash on top,
   and that composite is a shade darker and visibly grained. A flat fill of the
   base colour next to it is lighter and smooth, so the join read as a stripe
   even though the two values are nominally the same.

   Painting the noise into the SVG is not an option: `preserveAspectRatio="none"`
   stretches the 1440x90 viewBox to the section width by 70px, so an SVG
   `<pattern>` would stretch with it and the grain would smear.

   So the shape is inverted instead. The SVG element takes the light ground as a
   CSS background — undistorted, tiling at natural size, byte-identical to the
   section above because it is literally the same declaration — and the path now
   fills navy *below* the curve rather than light above it. The seam that is left
   is navy against navy, which cannot be seen.

   The tile phase does restart at the element boundary. It is random grain, so a
   phase discontinuity has nothing to reveal; a directional texture would need
   `background-attachment: fixed` or a shared parent instead. */

/* ── THE ONE-FIELD SEAM IS GONE, 09-22. THE JOIN IS THE PAGE DEFAULT AGAIN. ──
   "follow the typical spacing we do from one section to the next so the page
   feels super consistent."

   Both pairs stopped being one field on 09-22, when #hv4-problem and
   #hv4-correction went flat white (see the trial block far above). Once the
   colour changes across the join, the colour does the separating, and there is
   no longer any reason for these joins to be shorter than every other join on
   the page. So the four overrides came off and .hv4-section's 6rem a side —
   192px — now runs at EVERY join. One number, no exceptions.

   *** THE WHOLE HISTORY OF THE DELETED NUMBER, because it moved five times and
   every move was reasoned. It only ever existed to solve "two sections, one
   ground". If the flat white is reverted, the seam has to come back with it. ***

     192px  6rem+6rem, the .hv4-section default. On a SHARED ground this read as
            marooned — the cost headline looked left behind rather than
            introduced. *** That verdict does NOT transfer to a colour-change
            join, which is what this now is. ***
      80px  Too close. The headline read as a caption on the graphic above it.
     112px  Settled for several days. Then: "In both of the white sections that
            contain 2 different main headings I think we need something to
            separate them. Maybe even some space. It doesn't feel intentional."
     144px  Lasted hours. "let's add a little more white space between the
            sections in white."
     176px  Lasted a day. Removed with the shared ground.

   A 1px hairline was built at 112px and is recorded below; it was seen against
   a space-only version and the space won. The flat white is the third thing
   tried against the same complaint, and it is the only one that answers it by
   making the two sections actually different rather than further apart.

   *** IF THE FLAT WHITE IS REVERTED, RESTORE ALL FOUR VALUES AT ONCE ***
   — padding-bottom on #hv4-problem and #hv4-correction, padding-top on
   #hv4-cost and #hv4-delivery — or the two seams drift apart.

   #hv4-cost keeps `isolation: isolate`; that is for the ::before artwork layer
   below and has nothing to do with the seam. */
#hv4-cost {
    isolation: isolate
}

/* ── THE SEAM HAIRLINE: BUILT 09-21, REMOVED 09-21. DO NOT RE-ADD WITHOUT ASKING.
   ───────────────────────────────────────────────────────────────────────────
   "In both of the white sections that contain 2 different main headings I think
   we need something to separate them. Maybe even some space. It doesn't feel
   intentional."

   A 1px rgba(6,41,53,.14) hairline was drawn across each seam at the content
   width, one shared rule for both. Then: "I'm not opposed to the line breaks for
   the white sections, but is there somethign else we could try too? what about
   just extra space?" — and after seeing a space-only version beside it, "Let's
   remove the line breaks and see how a little extra spacing does." So the mark
   came out and the distance went 112 → 144 → 176px instead. Both seams are plain
   ground again.

   KEPT AS A COMMENT, NOT AS CODE, because the reasoning is what would be needed
   again and it is not obvious:

   - 1px NEUTRAL, NOT 2px COLOURED. feedback_rules_and_hairlines gives a 2px
     coloured rule to blocks that DIVIDE and a 1px neutral hairline to beats of
     ONE argument. The seam comment above says which these are. A 2px colour
     would say the field had been cut in half, which is the opposite of why the
     ground is shared at all.
   - A ::before ON THE CONTAINER, NOT a border-top on the section or the
     container. It has to be the width of the copy, and .la-container carries up
     to 144px of horizontal padding — a border would run the full 1440 and
     overshoot the headline it introduces by 288px. A block-level ::before is the
     content width for free and needs no positioning.
   - ONE RULE FOR BOTH SEAMS, so two marks of the same kind cannot drift to
     different weights.

   The exact declaration, if it is ever wanted back:

     #hv4-cost > .la-container::before,
     #hv4-delivery > .la-container::before {
         content: ""; display: block; height: 1px;
         background: rgba(6, 41, 53, .14); margin-bottom: <half the seam>
     }

   Note the margin has to be re-derived from the seam, not copied: it was 3.5rem
   when the sections' padding was 3.5rem, and the point was that the seam did not
   get taller for the mark. */

/* The SECOND one-field seam is gone too, 09-22, with the first — see the long
   note on #hv4-cost above, which is the single account of the number and is not
   repeated here so the two cannot disagree. #hv4-correction is now flat white
   against #hv4-delivery's textured paper, so the join separates on colour like
   every other join and takes .hv4-section's 6rem a side.

   The relationship between these two is unchanged and still close — get
   production to match the pilot, and then here is how you do it in ninety days,
   one argument in two beats. The seam was the way that closeness used to be
   shown. It is now shown by the two grounds being a half-step apart rather than
   a full one: flat white against textured white is the smallest colour change
   on the page, which is the right size for a paragraph break.

   NOTHING HERE IS FOR THE MOUNTAIN ANY MORE. This rule used to carry
   `isolation: isolate` and `overflow: hidden` for an ::after holding the peak;
   both are gone 09-17 with "this can bleed into the next section no problem".
   The artwork now belongs to .hv4-field, the wrapper around this section and
   #hv4-delivery — see below. `position: relative` still comes from
   .hv4-section and is not this rule's to remove. */

/* THE MOUNTAIN, moved here from #hv4-why on 09-17: "let's move that mountain
   into the how to get production to match part."

   It kept its compositing and lost its geometry, and both halves of that are
   deliberate.

   KEPT: `mix-blend-mode: multiply` at .16. The .webp has no alpha channel — it
   is dark linework on a white ground — so multiply is what drops the white away
   and leaves the peaks. This section is the textured near-white paper, which is
   the ground multiply is actually good on; it was becoming a no-op against the
   navy #hv4-why was about to become, which is half the reason the artwork moved.

   THE SUMMIT IS PLACED AGAIN, 09-17: "I want the mountain peak more in this
   area and bigger" — pointing at the band the headline sits in.

   The first version here was deliberately unplaced: full width, bottom-anchored,
   `height: min(45.167vw, 100%)` with `cover`, which at the artwork's own aspect
   ratio (271/600) is a 1:1 fit that crops nothing. That put the summit at 48% of
   the width and about 83px down — near the top already, but at 1x it read as
   scenery behind the section rather than as a peak the headline sits against.

   So it is scaled and offset instead of fitted, and every number below is
   derived from the artwork rather than guessed. The source is 6000x2721 and its
   summit sits at roughly 49% across and 17% down.

     box          inset: 0 — the whole section, not a bottom strip. The
                  artwork is now taller than the section, so a bottom anchor
                  would drop the peak out of the top of the box.
     size         170% auto. Image width 170vw, height 170 x .4535 = 77.10vw.
     summit at    x .49 x 170  = 83.3vw    y .17 x 77.10 = 13.11vw
     wanted at    x 83.3vw                 y 3.51vw  (~1199, 51 into #hv4-delivery)
     position     83.3 - 83.3 = 0          3.51 - 13.11 = -9.6vw

   BIGGER AND LOWER, 09-17: "let's move the mountain bkg graphic down a bit more
   and scale it up a bit more." 150% -> 170%, and -10.8vw -> -9.6vw.

   LOWER AGAIN, 09-21: "can you bring the mountain down a bit more."
   -9.6vw -> -6.6vw, 3vw / 43px at 1440. Only the offset moved; 170% is
   untouched, which is the point of the trap paragraph below.

   THE TWO NUMBERS ARE NOT INDEPENDENT AND THIS IS THE TRAP. The summit sits at a
   FRACTION of the image, so scaling up pushes it down the viewport on its own:
   at 170% with the offset untouched the peak would already have fallen from
   0.76vw to 2.31vw. Only the remaining 1.2vw is the "move it down" half of the
   instruction. Anyone re-reading this and changing the scale alone will move the
   peak vertically without meaning to.

   IT STILL CLEARS THE HEADLINE, which was an earlier explicit instruction ("it
   should go above the headline"). Re-measured at 1440 after the 09-21 move:
   summit at y=94 into #hv4-delivery, headline box 113-163. Still above it, by
   19px.

   THE OLD NOTE HERE SAID -9.6vw WAS "THE LIMIT" AND IT WAS WRONG, because it
   only ever looked at y. The summit is at x=1200 and the headline's longest line
   ENDS at x=793 — they are 400px apart horizontally and cannot collide whatever
   y does. The real limit is the section's own top edge: the peak leaves the box
   once the offset passes about -1vw, and #hv4-delivery has `isolation: isolate`
   so nothing bleeds up into #hv4-correction to catch it. Measure x before
   calling a vertical move unsafe.

   Scaling also walked the summit 142px RIGHT, 1058 -> 1199, because x is derived
   from the same fraction. That is accepted rather than corrected: x is pinned to
   0 by a separate instruction ("so the edge matches"), and at .11 opacity the
   artwork is a texture behind the section rather than an object placed in it.

   THE SUMMIT IS IN #hv4-delivery NOW, 09-17, in two moves on the same day.

   First, "mountain peak needs to be in this region" took it off the
   #hv4-correction headline. It had been `-37.5vw -4vw` = x 518, y 109, dead
   behind "How to Get Production to Match the Pilot."

   Then, "let's move mountain down to be in the one slice section instead."
   #hv4-delivery runs 629-1667 inside the field. Its headline ends at x 978 and
   its lead at x 913, and the six numbered steps start at y 859 — so x 978-1297
   by y 629-859 is the open paper, and the summit went there. Same rule both
   times: the peak does not negotiate with copy, it goes where there is none.

   Then twice more, same day: "move the mountain in the slice section <-- this
   way so the edge matches and move it up a little more", and "it should go
   above the headline."

     the edge   x is now a flat 0, not a nudge. The artwork used to start 62px
                in from the left, which read as a misalignment against every
                other full-bleed thing on the page. 0 pins the image's left edge
                to the section's, and it still covers because 2160 > 1440. This
                is the one number here that is NOT derived from the summit — it
                is derived from the frame, and the summit follows it to x 1058.
     above it   -10.8vw puts the summit 11px into #hv4-delivery, and the "One
                Slice, Ninety Days, Then Again" headline starts 56px into it. The
                peak therefore crests in the band of paper above the headline
                rather than beside it, at every width, because both numbers are
                now measured from the same section's top edge.

   Two placements were tested and rejected along the way, both in
   #hv4-correction and both for the same reason — cresting just above the card
   row, and sitting inside it. The three cards are SOLID teal, so anything
   behind them is simply gone, and the second of those left the top of the
   section bare with a summit nobody could see.

   The artwork now renders 533-1513 inside the field, so it is effectively
   wholly inside #hv4-delivery: the ~96px of it that reaches up past y 629 is
   the picture's own white sky, which multiply drops. .hv4-field therefore no
   longer has to SPAN anything for the artwork's sake. It stays, because the
   stacking arrangement below is what keeps the copy on top, and unpicking it to
   save one wrapper is not worth the regression.

   The foothills still run off the bottom, which is correct: this is a peak on a
   page, not a landscape with a base. The positive x means the image starts 62px
   IN from the left edge — it still covers, because 62 + 2160 > 1440.

   These numbers are a pair with the 150%. Change the scale and all three move
   together, or the peak walks off the heading. They are unchanged by the move to
   .hv4-field, because the field's top edge IS #hv4-correction's top edge — the
   wrapper adds no padding and the section is its first child. The peak is still
   measured from the same origin.

   THE BOX IS #hv4-delivery ITSELF, and it used to be .hv4-field.

   The wrapper was built on 09-17 for "this can bleed into the next section no
   problem" — one layer spanning correction + delivery, because at the time the
   summit was on #hv4-correction's headline and the foothills ran down through
   the seam. It is not needed for a summit that lives in #hv4-delivery, and
   keeping it actively broke phones: `inset: 0` on the field meant the offsets
   were measured from #hv4-correction's top, and a vw offset cannot track a
   field whose height goes 1667 → 3022 when the columns stack. At 375 the
   artwork rendered at y 139 of a 3022px field — the top of the WRONG SECTION.

   Hanging it on #hv4-delivery makes the origin the section's own top edge at
   every width, so one pair of vw numbers holds from 375 to 1440. The artwork is
   979px tall on a 1038px section at 1440 and fits inside it, so nothing needs
   to bleed anywhere.

   THE STACKING IS THREE VALUES, ONE MECHANISM:
     isolation: isolate  on #hv4-delivery — the blend group. Without it multiply
                         reaches out past the section, and the part of the
                         artwork with no ground behind it composites against
                         transparency, which turns the webp's white into a
                         visible haze. It is safe HERE, on the element that owns
                         the ::after; it was not safe when the artwork was on
                         the parent, because then it trapped the container
                         underneath the peak. That old warning does not apply
                         any more and is the reason this paragraph exists.
     z-index: 0          on the ::after — above the section's own ground.
     z-index: 1          on .la-container — the copy back on top of the artwork.
                         Still written against .hv4-field>section so it covers
                         #hv4-correction too, which is what keeps the wrapper.

   multiply: the .webp has no alpha, it is dark linework on white, so multiply
   is what drops the white away, and .hv4-section--paper is the near-white ground
   it needs to do that against.

   OPACITY .16 -> .11, 09-17: "the one slice ninety days mountain is a tad too
   dark can we lighten it more". The blend mode is NOT the lever here and must
   not be touched to answer this — multiply is what removes the sky, opacity is
   how hard the linework presses on the paper. Same division of labour as the
   rocks photograph below, which states it explicitly. Dropping multiply would
   put the webp's white back and turn the section grey.

   .11 rather than a smaller step because .16 -> .14 is not visible on linework
   this fine: the drawing is 1px strokes on near-white, so the only thing opacity
   changes is the darkest pixel in a stroke, and a 2-point move there is under a
   just-noticeable difference. The floor is about .08, where the summit stops
   reading as a shape at 1024 and below.

   THE BOTTOM IS MASKED, 09-18: "let's fix this", against a crop of the seam
   where a hard horizontal line and a band of clean paper sat between the mountain
   and the teal wave below it.

   THE SCALE-UP IS WHAT CAUSED IT, and the paragraph above used to end by saying
   the artwork "fits inside the section, so nothing needs to bleed anywhere."
   That stopped being true at 170%. At 1440 the image renders 2448 x 1110 and
   starts at -138, so it ends at 972 of a 948px section — it is cut 24px short of
   its own bottom edge, by the section boundary, in the middle of the foothills.
   Below that boundary is #hv4-why's .wave-top, which carries --paper and no
   artwork, so the drawing stopped dead and a clean strip started.

   The mask fades the last 30% of the box to nothing, so there is no edge left to
   see. It is the RIGHT lever rather than scaling back down: the size and the
   offset are both settled values from "move the mountain down a bit more and
   scale it up a bit more", and the fault was never the size — it was the cut.
   IF THE SCALE OR OFFSET MOVES AGAIN, CHECK THIS SEAM. The mask only needs to
   start above wherever the image's own bottom edge now falls.

   Not a fade on .wave-top instead: the wave stretches its viewBox and the
   artwork does not, so anything that tried to continue the drawing across the
   two elements would smear. Same problem the fill/mask notes at the top of this
   file are about. */
.hv4-field {
    position: relative
}

.hv4-field>section>.la-container {
    position: relative;
    z-index: 1
}

#hv4-delivery {
    isolation: isolate
}

#hv4-delivery::after {
    content: "";
    position: absolute;
    inset: 0;
    background: url("psd_assets/facades/Why it Works Mountain.webp") no-repeat 0 -6.6vw/170% auto;
    -webkit-mask: linear-gradient(to bottom, #000 70%, transparent 100%);
    mask: linear-gradient(to bottom, #000 70%, transparent 100%);
    mix-blend-mode: multiply;
    opacity: .11;
    pointer-events: none;
    z-index: 0
}

/* The rocks-and-surf photograph, bottom-LEFT of the cost section.

   It moved sides on 09-17 with the grid it shares the section with ("what if we
   flipped this"). The photo has always sat under the prose column and never
   under the chart card, and that is the constraint, not the corner: a
   greyscale photograph multiplied under a white card would put a second texture
   behind the plot. The prose is the only thing in the section that can carry it.
   So when the columns swapped, the photo swapped with them, and all three of its
   directional values had to turn over together — the anchor (right -> left), the
   background position (right bottom -> left bottom) and the first mask ramp
   (to right -> to left). Miss any one of the three and the fade runs off the
   wrong edge.

   It is a ::before rather than a fourth layer on the shared background
   declaration above, and that is the whole reason it exists as a pseudo-element:
   `#hv4-problem, #hv4-cost, #hv4-conditions .wave-top` has to stay
   byte-identical across all three selectors or the wave seam comes back. The
   photo belongs to this section only, so it cannot go in a rule two other
   elements read.

   mix-blend-mode: multiply, not opacity. The source is a near-white greyscale
   frame, so multiplying it into the ground drops the white away to nothing and
   leaves only the rock darkening the section. With opacity there is always a
   faintly rectangular wash where the sky is; with multiply the image has no
   edges except the ones the subject has. `isolation: isolate` on the section is
   what keeps that blend from reaching the sections above and below it.

   THE WHOLE PHOTOGRAPH LIVES IN 38 LEVELS OF GREY, AND THAT IS THE FACT EVERY
   OTHER VALUE HERE IS DERIVED FROM. Sampled off a canvas at natural size
   (1920x1080), luminance means:

     flat sky, y < 100                                   247
     rock, first 40px below its own skyline              221
     rock, 350-600px below its own skyline               209
     the wave and rubble, x 300-1100, at EVERY depth     226

   Three things follow, and 09-30 — "the top of the mountain shouldn't be faded,
   only the bottom and we need to use a better part of the mountain here" —
   is all three at once:

   1. THE TOP OF THE ROCK IS THE LIGHTEST PART OF THE ROCK. 221 at the skyline
      against 209 deep. The fade at the summit is IN THE PHOTOGRAPH; it is not
      the mask. Do not go looking for a gradient to shorten — the top ramp ends
      at box y 38 and the peak sits at 52, measured, so nothing masks it.
   2. THE LEFT HALF IS DEAD. 226 flat, 21 levels off the sky and it never
      densifies with depth, so the wave is a grey haze at any strength. That is
      the "better part of the mountain": there isn't one on the left, so it gets
      cropped rather than strengthened.
   3. THE SKY IS ONLY 8 LEVELS OFF WHITE AND WAS STILL DARKENING THE GROUND.
      At the old .6 the sky pressed 1.9% and the summit 8.0% — a 4:1 ratio
      between the subject and the blank behind it, which is what "faded" looks
      like. filter: contrast(1.15) lifts anything above 238 to pure white, so
      the sky now presses 0.0% and the summit has a hard edge against it.

   contrast(1.15) and opacity: 1 are a PAIR and neither survives alone. Contrast
   pivots at 128 and everything in this image is above it, so the lift also
   pushes the rock lighter; opacity goes back to full to pay it back. Measured
   darkening of the ground, before -> after:

     sky      1.9% -> 0.0%      summit   8.0% -> 7.9%
     wave     5.7% -> 5.6%      rock base 11.0% -> 13.3%

   So the summit and the wave land where they already were, the base gets the
   weight it should always have had, and the blank sky stops competing. Raising
   opacity WITHOUT the contrast is the "full strength was too much" that was
   rejected on 09-17 — it presses the sky to 3.1% and the rectangle comes back.

   ⚠️⚠️ 10-20 — opacity IS .8 NOW, AND THE "PAIR" ABOVE IS WEAKER THAN IT READS.
   *** "why don't we just lower the mountain opacity a little more and call it a
   day" *** — for the governance copy's contrast, below.

   ⚠️ LOWERING OPACITY CANNOT BRING THE FADED SKY BACK, which is what the
   paragraph above appears to forbid and does not. The sky's pressure is 0.0%
   WITH the contrast lift, and opacity SCALES pressure — zero times anything is
   still zero. The 09-17 rectangle came from the no-contrast state, where the sky
   had 3.1% to scale. So the pair binds in ONE DIRECTION ONLY: contrast cannot be
   removed while opacity is high, but opacity can come down freely underneath it.
   ⚠️ Read a constraint for the direction it was written in before obeying it.

   All four pressures scale by .8:  sky 0.0  summit 6.3  wave 4.5  rock base 10.6

   ⚠️⚠️ THIS IS AN IMPROVEMENT, NOT A FIX, AND IT WAS CHOSEN KNOWING THAT. The
   governance copy's worst ratio goes 3.50 -> 3.88 against AA's 4.50. ⚠️ THE
   PHOTO CANNOT REACH 4.50 AT ANY OPACITY WORTH SHIPPING — swept against the real
   pixels (image loaded, contrast applied, all three mask ramps applied,
   multiplied onto the ground, every pixel under .hv4-gov walked):

     opacity   1    .8    .7    .6    .5
     worst   3.50  3.88  4.08  4.28  4.49   <- .5 is half the mountain and STILL
                                               short, so do not keep reaching for
                                               this lever expecting it to arrive.

   The lever that DOES reach it is the type: .hv4-gov p at #3b525a (the same
   grey-teal 20% darker) measures 4.71, and the body navy #062935 measures 8.70.
   Offered and not taken — the softness of the grey was the point. If this comes
   back, change the ink, do not chase the photo further.

   ⚠️ 3.50 WAS THE REAL NUMBER AND 3.74 WAS MINE, WRONGLY. The earlier figure was
   modelled from the recorded 13.3% rock-base pressure rather than sampled; the
   darkest pixel actually under the column composites to rgb(194,196,196). Sample
   the image, do not infer from a note about it.

   The mask does the rest of the edge work, on all three sides that have one:
   transparent along the inner edge so the photo never runs under the chart card,
   transparent at the bottom so it does not stop dead at the join with
   #hv4-conditions .wave-top — that edge is the one place on the page where a
   hard horizontal line has already caused a visible seam — and transparent
   across the top, which was missed the first time and showed immediately.

   The top edge is worth explaining, because multiply does not hide it. The
   photo's sky is only a shade off the ground, so the *value* step across that
   edge is tiny; what gives it away is texture. The section ground is a noise
   tile and the sky is a smooth gradient, so the boundary reads as a crisp line
   between grain and no grain even where the two are nearly the same colour. Any
   edge of this image needs a fade whether or not the tones meet.

   THE FRAME IS `auto 118%` AND `right 24%`, NOT `cover`, AND THE TWO ARE ONE
   DECISION. Read them together or the summit goes off the top of the box.

   `cover` renders 1138x640 at EVERY width — the box is 640 tall everywhere
   (min(80%, 640px)) and never wider than 921.6, so height always binds and the
   scale is pinned at 0.5926. Only the left crop moves: source x 365 at 1440,
   1057 at 800, 1515 at 375. At 1440 that puts 935px of the dead wave region in
   frame, more than half the box, directly under the prose. `auto 118%` scales
   to 0.6993 and starts the frame at source x 602 instead — still the widest
   crop of the three, but the haze is pushed into the shallow end of the left
   ramp where it reads as ground rather than as water.

   Height-relative, not width-relative, and that is load-bearing: the render is
   1343x756 at every viewport, so the zoom is fixed and only the window changes.
   A `%` size would be width-relative and at 375 would render 600x337 into a
   640-tall box — the same failure the Why mesh had, a band of bare ground.
   Anything of the form `auto N%` with N >= 100 always covers, because the
   render is 1.778x its own height and the box is never wider than 921.6.

   ZOOMING FORCES THE VERTICAL POSITION TO MOVE WITH IT. `bottom` anchors the
   image's last row to the box floor, so at 118% the extra 116px of render all
   comes off the TOP — source y 0-166, and the peak is at y=114. Anchored bottom
   this crop decapitates the mountain. `24%` puts source y 40 at the box top and
   y 955 at the box bottom, which lands the peak at box y 52.

   The top band is 6% and the number falls straight out of that: 6% of 640 is
   38px, so the ramp is fully opaque 14px above the peak and runs entirely
   through flat sky (every row above y=110 of the source is blank, darkest pixel
   236-240). It was 10% under `cover`, where the peak sat at box y 68 — the same
   clearance, re-derived for the new frame. RE-DERIVE IT AGAIN IF EITHER THE
   SIZE OR THE POSITION MOVES; the two numbers are not independent.

   That is the whole trick here: the fade has to live entirely inside the flat
   sky. A 30% band was the first attempt and it dissolved the peak, which is the
   one part of the photograph doing any work. Inside the sky the ramp is
   invisible anyway — and now doubly so, since contrast(1.15) has already taken
   the sky to pure white. The fade is not hiding a tonal step, it is hiding the
   loss of the noise tile's grain.

   Three layers intersected, so a pixel survives only where all three are opaque.
   -webkit-mask-composite takes source-in per additional layer, which is the
   prefixed spelling of the same thing for older WebKit.

   Sized off the section rather than the container so it bleeds to the viewport
   edge. pointer-events: none because it sits over the governance column's lower
   half and must not eat clicks.

   bottom: 5rem, not 0. Anchored to the section floor the rock's peak sat below
   the chart and the frame read as a strip along the bottom edge; 80px up puts
   the peak level with the upper half of the card, which is where the eye already
   is. It also keeps the whole of the bottom fade clear of the join with the wave.

   THE SIDE IS NOT A FREE CHOICE. The photo has to sit under the PROSE and never
   under the white chart card, so it follows .hv4-cost-grid. Three values turn
   over together whenever that layout changes: the anchor (left/right), the
   background-position, and the direction of the FIRST mask ramp. Changing fewer
   than three puts a greyscale photograph behind the chart. Right, as of the
   09-17 "let's put this back".

   *** THE PEAK MOVED DOWN AND RIGHT ON 10-01, AND THE TWO MOVES USE DIFFERENT
   LEVERS ON PURPOSE. *** "also the peak needs to move down", then "move the
   mountain peak -->". DESKTOP AND TABLET ONLY; the <=767 block below restores
   both, because the phone frame two comments down is hand-derived and the only
   screenshot that came with either note was the governance column, which is a
   layout that does not exist at 375.

   DOWN is 24% -> top, and 27.6px is ALL THE TRAVEL THERE IS. The slack is
   imgH - boxH = 0.18 x 640 = 115px, of which 24% was already spent. At `top`
   the image's first row is flush with the box's, the peak lands at box y 76.9
   (source y 110 at the 0.6993 scale) and there is no further down without
   re-zooming or moving the box, neither of which is worth it for this.

   The 6% top band was re-derived as the comment above demands and it SURVIVES
   UNCHANGED, which is the answer and not a skipped step: 38.4px of ramp against
   a peak at 76.9 is 38.5px of full opacity above the summit, up from 14px, and
   the ramp now runs through source y 0-55, where the darkest pixel in any row
   is 237. Moving the peak DOWN can only ever add clearance here; it is moving
   it up that would need the band re-cut.

   RIGHT IS THE BOX, NOT THE BACKGROUND, and that distinction is the point. The
   image is already anchored `right`, so background-position could only move it
   right by going BELOW 100% — which slides the frame left over the dead wave
   region while the `to right` ramp, being a percentage of the box, stays put.
   The gap between the two opens as a flat opaque smudge: at 1440 the ramp ends
   at 534px and the mountain's first ridge pixel lands at 538px, i.e. they are
   tuned to meet. Compensating by raising the ramp to 69% re-balances 1440 and
   makes every other width worse — the alignment error goes as 1/(pos - ramp),
   so 1.00/0.58 holds across the range and 0.75/0.69 holds at exactly one width.

   `right: -6rem` moved the box and therefore the ramps with it, so every
   internal relationship was byte-identical and only the window travelled. It
   also landed the same way at every width: the image is right-anchored inside a
   right-anchored box, so the peak sat a constant 110px from the viewport edge
   at 1440, 1200 and 900 alike, where it was 206px.

   *** AND ON 10-16 THE HORIZONTAL HALF WAS ASKED BACK: "this is still off on
   the home page it needs to go <-- this way... where we had it before was a
   good position." *** So `right` is 0 again, which is the pre-10-01 geometry
   exactly — not an approximation of it, because the box is the only thing that
   ever moved.

   ⚠️⚠️ THE VERTICAL HALF OF THE 10-01 MOVE STAYS. The two levers are listed
   separately above precisely so one can be reversed without the other: `top`
   (the DOWN half) is untouched, only `right` goes back. Reversing both would
   silently re-raise the peak into the 6% ramp, and nobody asked for that.

   MEASURED AT 1600, summit distance in from the VIEWPORT'S right edge, against
   the governance copy's right edge at 144:

       right: -6rem      summit 111     33px OUTSIDE the column   <- "too far"
       right: 0          summit 207     63px inside it            <- restored

   ⚠️ 33px was the exact gap in the screenshot, and that is how the width was
   identified: the note was read as a PHONE complaint first and it is not one.
   The governance two-column layout does not exist below 1200, so a screenshot
   containing it is never a phone — check which layout the picture shows before
   deciding which @media block to edit.

   ⚠️ THE "FLAT OPAQUE SMUDGE" WARNING TWO PARAGRAPHS UP DOES NOT APPLY HERE.
   That failure is specific to moving the image with background-position, which
   slides the frame while the percentage ramp stays put. Moving `right` carries
   the ramp with it, so the 58%/mountain alignment at 1440 is preserved.

   With `right: 0` nothing hangs off the right any more, so the .site-wrapper
   clipping the old 96px relied on is no longer load-bearing. Still
   overflow: hidden on both axes; scrollWidth == innerWidth re-confirmed. */
#hv4-cost::before {
    content: "";
    position: absolute;
    right: 0;
    bottom: 5rem;
    z-index: 0;
    width: min(64%, 940px);
    height: min(80%, 640px);
    background: url("psd_assets/wave-rocks.jpg") no-repeat right top/auto 118%;
    filter: contrast(1.15);
    mix-blend-mode: multiply;
    /* .8, not 1 — 10-20. The full working is in the comment block above: it buys
       the governance copy 3.50 -> 3.88 and the sky stays at 0.0% because the
       contrast lift zeroes it before opacity scales it. */
    opacity: .8;
    -webkit-mask-image: linear-gradient(to right, transparent, #000 58%), linear-gradient(to top, transparent, #000 34%), linear-gradient(to bottom, transparent, #000 6%);
    mask-image: linear-gradient(to right, transparent, #000 58%), linear-gradient(to top, transparent, #000 34%), linear-gradient(to bottom, transparent, #000 6%);
    -webkit-mask-composite: source-in, source-in;
    mask-composite: intersect;
    pointer-events: none
}

/* ON A PHONE THE LEFT RAMP WAS EATING THE SUMMIT, AND NO CHOICE OF SIZE OR
   POSITION COULD HAVE FIXED IT. The peak sits a FIXED 210px in from the box's
   right edge — 300 source px at the 0.6993 scale, and the scale is height-
   relative so it does not move with the viewport. The `to right` ramp is a
   PERCENTAGE of the box. So the peak is only fully opaque when

       boxW - 210 >= 0.58 * boxW    ->    boxW >= 500px

   and at 375 the box is min(64%, 940px) = 240px. The peak landed at box x 30,
   inside a ramp that does not reach full black until 139 — 78% masked out. It
   was 55% masked under the old `cover` frame too, so "the summit at every
   width" has never actually been true on a phone; it is not a regression from
   the 09-30 reframe, it is a bug that reframe exposed.

   Two numbers move together and both are forced. Full width gives 375 - 210 =
   165px of room to the left of the peak, so the ramp has to finish inside it:
   36% of 375 is 135px, which clears the peak by 30px. IF THE ZOOM CHANGES, 210
   CHANGES AND THIS 36% HAS TO BE RE-DERIVED.

   Full width does NOT break the "never under the white chart card" rule, and it
   is worth writing down why rather than trusting it. At 375 the card ends 638px
   into the section and the box starts at 630 — an 8px overlap. That strip is
   inside the 38px top ramp (alpha <= 0.21) AND it is source y 40-51, flat sky at
   luminance 247, which contrast(1.15) has already taken to pure white. Nothing
   renders there at all. Re-check it if the card or the section height moves.

   The other two ramps are restated only because mask-image is one property and
   cannot be partially overridden; they are unchanged. mask-composite is a
   separate property and still applies from the base rule.

   THE 10-01 PEAK MOVE IS UNDONE HERE, BOTH HALVES OF IT. `right: -6rem` is 26%
   of a 375px viewport against 6.7% of 1440, so the same declaration is a nudge
   on a desktop and a shove on a phone — and it would push 96px of a box that is
   already only 375 wide off the edge, taking the right flank with it. The
   vertical goes back too: the 36% ramp and the 30px it clears the summit by
   were derived against this frame, and `top` would move the peak into the strip
   that overlaps the chart card, which the note above certifies as blank only
   for source y 40-51.

   This is the 09-30 mesh lesson, which cost a day: A BASE-RULE PROPERTY IS NOT
   A DEFAULT, IT IS A DECLARATION AT EVERY WIDTH THAT DOES NOT OVERRIDE IT. Ask
   which screen the screenshot was taken on before writing anything.

   *** 10-14, AND THIS CLOSES THE "LOWERING THE MOUNTAIN ON PHONES" OPEN ITEM
   THIS COMMENT USED TO CARRY: "I don't like the placement of this mountain on
   mobile. let's move it down." ***

   MEASURED FIRST, at 375. The box ran section y 618-1258 and the summit landed
   at 668 — the governance h3 starts at 694, so the peak was 26px ABOVE the
   heading and the rock sat behind the whole block (h3 694-751, then three
   paragraphs to 1274). That is the screenshot.

   ⚠️ THERE IS NO ROOM TO PUT IT BELOW THE COPY. The last paragraph ends at 1274
   and the section ends at 1338. "Down" on a phone can only mean *later in the
   block* — heading and opening paragraph on clean paper, rock rising behind the
   lower half. It cannot mean "under the text".

   THE BOX IS BOTTOM-ANCHORED, SO HEIGHT IS THE LEVER THAT MOVES IT DOWN. bottom
   stays at the base rule's 5rem (the note above derives it, and it is what keeps
   the bottom fade clear of #hv4-conditions' wave). Shrinking the box therefore
   moves only its TOP, downward: 27% of the section is 361px, so the box top goes
   618 -> 897, which is the last line of the first paragraph.

   ⚠️ SHORTENING THE BOX SHRINKS THE PHOTO, BECAUSE `auto 118%` IS HEIGHT-
   RELATIVE. 118% of 361 would render the mountain at 426px against the old
   755px — the move would read as the picture receding, not descending.

   ⚠️⚠️ AND THE FIRST ANSWER TO THAT — `auto 160%` — MOVED THE MOUNTAIN SIDEWAYS,
   10-16: "this is too far over. where we had it before was a good position."
   THAT IS THE LESSON IN THIS BLOCK AND IT IS WORTH THE SPACE: on a RIGHT-
   ANCHORED background, ZOOM IS ALSO A HORIZONTAL MOVE. The summit stands a
   fixed FRACTION of the image's height in from its right edge — measured 0.274,
   by sampling the jpg on a canvas and taking the topmost column that drops to
   90% of the corner sky value (summit at natural 1624,111 of 1920x1080; see the
   restored `auto 755px` below). Scale that fraction and the summit slides:

       zoom          imgH    summit in from right    summit x at 375
       118% of 640    755            207                   168     <- asked for
       160% of 361    578            158                   217     <- "too far"

   49px of unasked-for lateral travel on a 375 screen, and NOTHING in the rule
   named the x axis. ⚠️ Measure the summit, not the box, after any size change.

   THE FIX IS A FIXED PHOTO HEIGHT, NOT A NUDGE, and that is the important part:
   `auto 755px` is the old photo at its old SIZE, so the summit is 207px in from
   the right AT EVERY PHONE WIDTH, exactly as before. A `calc(100% + 49px)`
   shift was the obvious repair and is wrong — the needed shift measured 36 /
   48.5 / 64px at 320 / 375 / 430, because the section gets SHORTER as the
   phone gets wider, so a percentage zoom gives a different photo at every
   width. One constant cannot answer three numbers.

   ⚠️ 755px IS NOT A MAGIC NUMBER: it is 118% of 640, and 640 is the base rule's
   `min(80%, 640px)` CAP, which is what the phone band actually rendered — 80% of
   a 1210-1447px section is 968-1158, so the cap bound at every phone width and
   the photo was already a constant 755 there. A fixed length is therefore MORE
   faithful to what shipped than a percentage is. If the base rule's 640 ever
   moves, re-derive this.

   ⚠️ THE BOX IS NOW A 361px WINDOW ON A 755px PHOTO, which is the whole point —
   the box is what descends and the photo is what stays put. The bottom edge
   does not move at all (box bottom = sectionH - 80 = 1258, before and after);
   only the top does, 618 -> 897. What is lost is the lower slope, below photo
   row 361.

   AND THE SCROLL HAS TO GO BACK TO 0, which is the step that is easy to miss.
   The old 24% would scroll a box that is now only 361 tall and bury the summit
   in the top ramp. At `0` the photo's top sits on the box's top and the summit
   lands at box y 78 (0.103 x 755), section y 975 — the second paragraph.

   The top ramp goes 6% -> 10% because 6% of 361 is 22px, and a 22px fade-in on
   this size reads as an edge. 10% is 36px, leaving 42px of full opacity above
   the summit.

   THE LEFT RAMP IS UNCHANGED AND BACK TO THE CLEARANCE IT WAS DERIVED AGAINST:
   the summit is 207px in from the right, so at 375 it stands at x 168 while the
   36% ramp ends at 135 — 33px of clearance, the same ~30 it always had. ⚠️ Re-run
   this if the photo height moves again.

   `right: -2rem` is 10-20: "let's move the mountain img a little this way ->
   for mobile". A NEGATIVE right is the lever, not the `right: 5%` that the
   original worked design carried and that this block deliberately never applied
   — 5% is an INSET, which moves the mountain the wrong way and crops its right
   flank into the bargain.

   ⚠️ MOVE THE BOX, NOT THE BACKGROUND, and here that is measurable rather than a
   preference: the 36% left ramp is a share of the box's own width, so it travels
   with the box and the summit's clearance from it is IDENTICAL before and after
   — 33px at 375, 68px at 430, -2px at 320. Shifting `background-position` would
   have moved the summit while leaving the ramp behind, re-tuning a fade nobody
   asked to change.

   ⚠️ A NEGATIVE `right` OVERFLOWS, SO IT HAS TO BE CHECKED, NOT ASSUMED. Nothing
   on this page clips: `#hv4-cost`, `body` and `html` are all `overflow-x:
   visible`, so the box genuinely hangs 32px past the viewport. Verified at 320 /
   375 / 430 that `documentElement.scrollWidth` still equals `innerWidth` and
   that `scrollTo(400, y)` leaves `scrollX` at 0 — no horizontal scroll on a
   phone, which is the only thing that would make this unacceptable. ⚠️ Re-run
   both checks if this value grows.

   100% 0 and not `right 0`: the two-value form is unambiguous, but the
   three-value form of background-position reads a length after a keyword as an
   offset FROM that keyword, and that is a trap this file has already been
   caught by once. Percentages cannot be misread. */

/* 10-16, phone: "let's pull this mtn up a little higher", then "still feels
   low", then "a tad higher". `top: 589px` + `height: auto`, replacing `height:
   27%`; the top ramp goes back to 6%. `bottom` keeps the base rule's 5rem, and
   top + bottom + auto height is what makes the box fully constrained.

   ⚠️⚠️ THE BOX IS ANCHORED TO THE SECTION'S TOP NOW, AND THAT IS THE WHOLE
   POINT. Everything in this block used to be derived from the BOTTOM edge, and
   on this section the bottom edge is the worst possible reference: the section
   runs 1447 / 1338 / 1210 tall at 320 / 375 / 430, a 237px swing, because the
   governance copy rewraps. The content the mountain is actually positioned
   AGAINST barely moves at all — measured from the section top, the chart card's
   bottom is 638 / 638 / 621 and the h3 starts at 694 / 694 / 677, a 17px swing.
   Anchor to the thing that holds still. The summit now lands at 667 at every
   phone width instead of drifting 129px across the band.

   ⚠️ THE FIRST ANSWER WAS `bottom: 5rem -> 11rem` AND IT WAS THE WRONG LEVER.
   `bottom` does move the mountain up, but it takes the BOTTOM EDGE with it: the
   96px lift pulled the foot of the photo up off the last governance paragraph,
   so the picture got higher by getting SHORTER, and "still feels low" came back
   within the round. Moving the TOP edge is what was being asked for — the box
   grows upward, the bottom stays where it has always been, and because
   `background-position` is `100% 0` the photo's top rides with the box top, so
   the summit rises by exactly the growth and more of the lower slope appears at
   the same time.

   ⚠️ HEIGHT IS A VERTICAL-ONLY LEVER HERE ONLY BECAUSE `background-size` IS A
   FIXED LENGTH. `auto 755px` means the photo does not rescale when the box
   does, so the summit stays 207px in from the right no matter what the box
   does. If that ever goes back to a percentage, resizing the box becomes a
   sideways move again — which is the 49px of unasked-for lateral travel
   documented above.

   ⚠️ A PERCENTAGE RAMP IS A PIXEL FADE, SO RESIZING THE BOX STALES IT. The top
   ramp was only ever raised 6% -> 10% because 6% of the old 361px box was a
   22px fade that reads as a hard edge. The box is 541-778 tall now, so 6% is
   32-47px — the fade the original derivation wanted — and undoing that bump is
   part of this change, not a separate tweak.

   Measured (section height / box height / summit / opaque px above the summit):

       320    1447    778    667    31
       375    1338    669    667    38
       430    1210    541    667    46

   The summit sits 29px below the chart card at 320 and 375 and 46px below it at
   430, i.e. in the gap between the card and the h3 at all three, with the
   headline reading against the peak's shoulder. ⚠️ The ceiling here is the chart
   `figure`, which is opaque white: at `height: 50%` the summit went to 603 at
   430 and vanished behind the card entirely while still looking right at 375.
   That asymmetry is exactly the drift this anchor change removes.

   ⚠️ At 320 the box (778) is taller than the photo (755), so the photo ends 23px
   above the box's bottom edge. Not a seam: the 34% bottom ramp has already
   faded it out 246px earlier.

   ⚠️ UNCHANGED AND PRE-EXISTING, NOT INTRODUCED HERE: at 320 the summit stands
   at x 113 and the 36% left ramp ends at x 115, so the peak sits right on the
   ramp edge where at 375 it has 33px of clearance. Nothing in this edit touches
   the horizontal; flagging it so the next person does not read it as fallout. */
@media(max-width: 767px) {
    #hv4-cost::before {
        right: -2rem;
        top: 589px;
        height: auto;
        width: 100%;
        background-position: 100% 0;
        background-size: auto 755px;
        -webkit-mask-image: linear-gradient(to right, transparent, #000 36%), linear-gradient(to top, transparent, #000 34%), linear-gradient(to bottom, transparent, #000 6%);
        mask-image: linear-gradient(to right, transparent, #000 36%), linear-gradient(to top, transparent, #000 34%), linear-gradient(to bottom, transparent, #000 6%)
    }
}

/* 10-01, iPad Pro portrait: "the photo bkg could also be a bit bigger and higher
   up." Both halves, one band, 992-1199. Nothing outside the band moves.

   THE IMAGE IS ALREADY THE SAME SIZE ON AN IPAD AS ON A 1440 DESKTOP — the
   complaint is the BOX, not the photo. `background-size: auto 118%` is
   HEIGHT-relative and the box height is `min(80%, 640px)`, which pins at 640
   from about 800px of section height upward. So the photo renders 1343x755 at
   1024 AND at 1440, identically. What changes is the box WIDTH, `min(64%,
   940px)`: 655 at 1024 against 922 at 1440. The iPad is not showing a smaller
   mountain, it is showing a narrower slice of the same one, and the slice is
   cut off the LEFT because the image is anchored `right top`. Raising the zoom
   is therefore the only lever that puts more mountain in a fixed-width box.

   140% and not more. The summit is at source (1625, 111) of a 1920x1080 file,
   i.e. 295px in from the right edge, so on screen it stands
   `295 x (zoom x 640 / 1080)` left of the box's right edge — 210px at 118%,
   245px at 140%. The `to right, transparent, #000 58%` ramp means the left 58%
   of the box is still fading in, so the summit is only fully opaque while
   `0.42 x boxW >= 245`, i.e. BOXW >= 583. That single inequality is the whole
   breakpoint: 992 gives 635 (22px of margin, the tightest rung and the reason
   the floor is 992), 1199 gives 767 (80px). 768 would give 491 and the peak
   would sit inside the ramp, half-dissolved — which is why this block does NOT
   reach down to the tablet rung. IF THE ZOOM CHANGES, 245 CHANGES AND THE 583
   HAS TO BE RE-DERIVED.

   "Higher" is bought with `bottom` and not with background-position-y, because
   the `to bottom, transparent, #000 6%` ramp fades the top 38px of the box:
   sliding the image up inside a stationary box walks the summit into that
   ramp, while moving the box takes the ramp with it. At 8.5rem the box top
   rises 57px and the summit sits 92px below it — clear of the 38px ramp with
   54px to spare. Measured at 1024: box top 113 -> 57, summit (914, 191) ->
   (875, 149), image 1343x755 -> 1593x896. 27% bigger, 42px higher.

   Verified it does not wash over the white chart card. `.hv4-spend` ends at
   x 517 and the box starts at 465, so 52px overlap — but that is the first
   7.9% of the box, where the left ramp is still at alpha 0.125. Unchanged by
   this rule, because the mask is box-relative and the box's LEFT edge did not
   move. Same figure at 992 (0.125) and 1199 (0.184).

   1200 is deliberately untouched: the desktop framing was approved this
   morning and still measures 118% / bottom 5rem / summit 116px right of the
   ramp. */
@media(min-width: 992px)and (max-width: 1199px) {
    #hv4-cost::before {
        bottom: 8.5rem;
        background-size: auto 140%
    }
}

/* The content has to be lifted above the photo explicitly. In-flow block content
   paints before positioned elements, so a z-index: 0 pseudo would otherwise sit
   on top of the headline and the governance copy. */
#hv4-cost>.la-container {
    position: relative;
    z-index: 1
}

/* ══ OPTION 8A, "THE BAR" — BUILT 09-18 FROM design_handoff_readiness_gap ═════
   The handoff arrived as a standalone HTML prototype plus a README marked
   high-fidelity, "recreate 1:1". This is that recreation. Everything the old
   graphic was — two white panels, four rows a side, a dimension channel between
   them — is gone, and the ~320 lines of CSS that held it are gone with it.

   WHAT REPLACED IT. Two 240px circles either side of a horizontal orange bar.
   The left circle is drawn closed (two concentric hairlines) and the right one
   open (a single dashed ring), which is the whole argument in the drawing: the
   pilot is a protected world with an edge around it, production is not. The bar
   between them is a dimension line with a tick at each end, so the distance is
   measured rather than implied, and the label and the statement sit on it. The
   section is named after a distance and this is the first version where the
   distance is an object you can point at.

   *** THE FOUR PAIRS ARE DELIBERATELY GONE. READ THIS BEFORE RESTORING THEM. ***
   Every previous version of this section was built around a pairing — Small
   scope opposite Eleven handoffs, Clean data opposite Contested data, with
   SCOPE / DATA / OWNERSHIP / ATTENTION named between them — and the project
   record says in as many words that every reversal here was a version that lost
   that pairing. This one loses all of it and that is not an oversight: the zip
   is titled "Design simplification for executives" and dropping the evidence is
   the simplification. Confirmed by instruction on 09-18 before a line was
   written. If this gets reversed, the pairing is what it will be reversed to,
   and the old markup and CSS are in git at 29f2385f.

   WHAT IT COSTS AND PAYS IN HEIGHT. The lead paragraph is deleted outright —
   its two halves became the two column bodies, which is the handoff's own idea
   and a good one: the sentence that praises the pilot now sits under the pilot
   and the sentence that kills it sits under production, so the copy is doing
   the diagram's work instead of narrating it ahead of time. The closing
   statement moved onto the bar for the same reason.

   THREE PLACES THIS DEPARTS FROM THE HANDOFF, all three deliberate and all
   three flagged to the client:
     1. GROUND stays #f4f6f6, not the handoff's #FFFFFF. #hv4-cost directly
        below shares that value on purpose so the two sections read as one
        field with no seam between them; making this one white would draw a
        line exactly where nothing is happening. The handoff's white was a
        standalone prototype on a grey page and had no neighbour to match.
     2. HEADLINE stays the page's h2 size, not the handoff's 52px. Five other
        h2s on this page are 41.76px. One at 52 would be the single most
        visible inconsistency on the page, and this whole pass was a
        consistency audit.
     3. PILOT'S LABEL is darkened — see the colour note on .hv4-rg-label--pilot.
   ══════════════════════════════════════════════════════════════════════════ */

/* NO EYEBROW AND NO HEADLINE OVERRIDE — both were in the handoff, both were
   built, both came out within the hour on 09-18: "keep the headline how we had
   it. no eyebrow."

   The eyebrow was a 14px Brandon cap reading "AI READINESS" above the h2. The
   headline override was `max-width: 20ch; text-wrap: pretty`, which replaced the
   authored <br> with a measure and let the line break itself. At 52px — the
   handoff's size — 20ch breaks into the two short lines the layout wants. At
   this page's 41.76px the same measure breaks into THREE, and a three-line
   headline over a wide graphic is why the section read as oversized. The <br>
   is back and it is the more reliable of the two mechanisms here, because it
   does not move when the type size does.

   So #hv4-problem's h2 now takes the page's shared h2 and nothing else, which
   is what every other section does. Do not re-add a cap here while the <br> is
   in the markup — that is two mechanisms fighting over the same line. */

/* The lead paragraph under the headline, restored 09-18. It is .hv4-lead, so
   the measure is the page's 66.67% and every other property comes from there;
   only the distance down to the graphic is declared here. */
.hv4-rg-intro {
    margin-bottom: 2rem
}

/* THE SCALE IS DRIVEN BY --rg-ring AND NOTHING ELSE. The circles, both side
   columns, the gap column's height, the bar's vertical position and the two
   text blocks' offsets all read it, so the graphic resizes from one number.

   *** 180px, NOT THE HANDOFF'S 240px, 09-18: "this feels huge." *** At 240 the
   two circles were the largest objects on the page and they are empty — a
   240px ring with nothing inside it is a lot of drawing for one word. 180 keeps
   them reading as circles at a glance and takes 60px off the section outright.

   The column tracks ARE the ring, so the circles are a fixed size and the GAP
   takes whatever is left. That is the correct way round for a graphic whose
   subject is the middle: widen the container and the thing that grows is the
   distance being measured.

   margin-top 3rem, down from the handoff's 5rem, because there is a lead
   paragraph above the graphic again and 80px was sized for the case where the
   headline sat directly on it. */
/* *** THE GRAPHIC IS CAPPED AND CENTRED, IT DOES NOT RUN THE CONTAINER. ***
   09-18: "this doesn't need to extend across the grid let's bring it in more so
   it's not so wide." At container width the gap column was ~595px carrying a
   400px paragraph, so the statement floated loose inside a band much wider than
   itself and the two circles were pushed to the far edges of the page.

   880px is chosen off the STATEMENT, not off the container: 180 + 40 + 400 +
   40 + 180. The gap copy is capped at 40ch, which measures ~400px at 20px Leo,
   so the middle track and the paragraph in it are now the same width — the bar
   spans exactly the block of text it is labelling. Widen this and the copy
   stops filling the span; narrow it and the copy starts wrapping tighter than
   its own cap.

   Centred rather than left-aligned under the headline. The graphic is
   symmetrical about the bar, so hanging it on the container's left edge would
   put the axis of symmetry off-centre on the page — the one alignment a
   dimension drawing cannot have. The headline staying left is deliberate
   contrast, not an oversight. */
/* *** THE COLUMN IS NO LONGER THE RING, 09-22: "this text can expand out
   further, it doesn't need to be so contained." *** --rg-ring still draws the
   circles and everything that is positioned against them; --rg-col is only the
   width of the two outer GRID TRACKS, i.e. the measure the captions wrap to.
   They were the same number until the captions became full sentences and 180px
   made each one a four-line ribbon narrower than the drawing above it.

   *** THE MIDDLE TRACK IS UNCHANGED AND THAT IS THE WHOLE TRICK. *** max-width
   went up by exactly twice the track's growth — 2 x 100px on 880 = 1080 — so
   the orange bar still measures 440px. The bar IS the readiness gap; paying for
   wider type by shortening it would be shortening the argument. *** IF EITHER
   NUMBER MOVES, MOVE THE OTHER: max-width = 880 + 2 x (--rg-col - 180). ***

   1080 still fits .la-container's 1155px content width at 1440 with room, so
   nothing clips and the graphic stays centred under the left-aligned headline. */
.hv4-rg {
    --rg-ring: 180px;
    --rg-col: 280px;
    display: grid;
    grid-template-columns: var(--rg-col) minmax(0, 1fr) var(--rg-col);
    align-items: start;
    gap: 2.5rem;
    max-width: 1080px;
    margin: 3rem auto 0
}

/* `color` here is load-bearing: the ring SVGs stroke with `currentColor` and
   inherit it from this column, so the label and the drawing above it cannot
   drift apart. Set the pair's colour HERE, not on .hv4-rg-label. */
/* *** THE COLUMN HAS TWO DIFFERENT GAPS, NOT ONE, 09-22: "that text under the
   pilot etc feels low." *** One flex gap of 1.25rem put the same 20px between
   ring→label and label→caption, so the caption floated free instead of hanging
   off its label. A label and the line it labels are ONE object; the ring is the
   thing being labelled. The two distances cannot be equal.

   The gap is now the SMALL one (label→caption) and the label carries the
   difference as margin-top, so ring→label is unchanged at 20px — .5 + .75 =
   1.25rem. Change either value and check the other: they add. */
.hv4-rg-col {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    color: var(--dark-teal)
}

/* The ring box. Square, and the SVG inside it scales to fill, so the whole
   graphic can be resized from this one value.

   *** THE WHITE DISC INSIDE EACH RING IS A PLACEHOLDER AND THIS IS TEMPORARY.
   *** The handoff specifies a photograph inside each — a "map" for pilot, a
   "trail/terrain" for production, circle-cropped at a 20px inset — and says they
   are not supplied yet. The rings were built empty on 09-18 rather than
   substitute existing site photography, then given a white disc the same day:
   "put placeholders in the circles like white circles."

   The disc is drawn IN THE SVG at r=100, which is exactly the handoff's 20px
   inset in a 240 viewBox, so it scales with --rg-ring for free and marks the
   real image's footprint rather than approximating it. When the artwork lands,
   either swap the <circle> for a <clipPath>ed <image>, or drop a
   `position: absolute; inset: 8.33%; border-radius: 999px; overflow: hidden`
   wrapper into this box — 8.33% being the same 20/240. Nothing else moves. */
.hv4-rg-ring {
    position: relative;
    width: var(--rg-ring);
    height: var(--rg-ring);
    flex: none
}

.hv4-rg-ring svg {
    display: block;
    width: 100%;
    height: 100%
}

/* *** THE CIRCLES CARRY CAPTIONS AGAIN — BUT NOT THE ONES THAT WERE DELETED.
   *** 09-21: "let's try putting some copy underneath pilot and production. then
   you can shorten the body copy under the headline. I wnt it to be easy to
   under stand."

   THE HISTORY THIS OVERWRITES. The handoff's captions were the lead paragraph
   split in half — "Every pilot works…" under the pilot, "Then it meets
   production…" under production. They were deleted on 09-18 because the lead
   came back and the two together printed the same four sentences twice, forty
   pixels apart. *** That is still true and those captions are still gone. ***
   What is here instead is the four condition pairs, which are not in the lead
   any more, so nothing is said twice.

   *** IT IS A PHRASE, NOT A LIST, AND THE LIST IS NOT COMING BACK. *** These
   were briefly two <ul>s, four one-word lines each: "I think this doesn't need
   lists underneath but maybe a sentence or a phrase under each one." Eight
   stacked short lines read as a spec sheet hung under a one-word label, in a
   section that already has two circles, a bar and a statement asking to be
   looked at. One line of prose per circle is a caption; four is a table.

   WHAT THE PAIRING RIDES ON NOW. The list version had row position doing the
   work — line 1 opposite line 1 was the pair — and needed `white-space: nowrap`
   to protect it. A phrase cannot use row position, so the pairing is carried by
   PARALLEL CONSTRUCTION instead: four comma-separated items, same order (scope,
   data, ownership, attention), no conjunction on either side, same length.
   *** That parallelism is now the only mechanism. *** It survives rewrapping at
   any width, which the row version did not — but it does not survive an "and",
   a fifth item, or a reorder on one side only.

   THE MEASURE IS THE TRACK, AND THE TRACK IS WIDER THAN THE CIRCLE. Still no
   max-width here: the caption fills its grid track, which is now var(--rg-col)
   at 280px rather than var(--rg-ring) at 180px.

   *** THE OLD RULE WAS "the text block is exactly as wide as the circle so the
   two read as one object." THAT RULE IS SPENT. *** It was written when the
   caption was three words on one line, where the circle's width is a generous
   measure. At a full sentence 180px is about 26 characters, which made each
   caption a four-line ribbon standing narrower than the drawing it belongs to —
   "this text can expand out further, it doesn't need to be so contained."

   Centring is what keeps the pair together now, not matched width. 280px is
   about 42 characters, so an 83-character sentence sets in two lines.

   The bar's length is protected by .hv4-rg's max-width moving with the track —
   see the note on that rule. Widening the columns alone would eat the middle
   track, and the orange bar IS the readiness gap.

   *** `text-wrap: balance`, 09-22: "the way the text wraps under the circles
   feels uneven." *** Centred type with a ragged fill reads as a mistake in a
   way that left-aligned type does not, because the eye has both edges to
   compare. Measured line lengths at 280px:

     pretty   261 / 220 /  72     209 / 212 / 129
     balance  177 / 176 / 200     195 / 197 / 157

   72px is a two-word stub under a 180px circle. balance drops the spread from
   189px to 24px on the left and 83px to 40px on the right.

   *** THE ARGUMENT THAT USED TO STAND HERE — "balance evens the lines by
   shortening the measure, which would make the two captions different widths
   under two identical circles" — IS WRONG, and it is worth knowing why. ***
   balance shortens the LINE BOXES, not the element. This <p> is the only child
   of a fixed 280px grid track, so it is 280px wide under either value; both
   captions measured 280 in all three modes. The rule only bites on a
   shrink-to-fit box, and this is not one.

   Both still set in three lines at 72px, so the pair stays matched.

   *** THE SELECTOR CARRIES A REDUNDANT-LOOKING `.hv4` AND IT IS LOAD-BEARING.
   DO NOT SIMPLIFY IT. *** `.hv4 p { text-wrap: pretty }` at the top of this
   file is the page-wide default, and it is (0,1,1) against a bare
   `.hv4-rg-caption`'s (0,1,0) — so `balance` was written, parsed, present in
   the cascade, and silently losing. Computed style still read `pretty` with
   the sheet visibly saying `balance`. This is the ONLY caption on the page
   that overrides that default, which is why nothing else in this rule ever
   revealed the problem: every other declaration here sets something `.hv4 p`
   does not touch. */
.hv4 .hv4-rg-caption {
    margin: 0;
    text-align: center;
    font-family: "leo", serif;
    font-size: .9375rem;
    line-height: 1.6;
    color: var(--text-muted);
    text-wrap: balance
}

/* *** BOTH LABELS ARE ONE COLOUR, 09-18: "let's make pilot and production both
   teal." They are not two tinted labels any more and the --pilot / --prod
   modifiers are gone with them. ***

   This is the right end of a chase. The handoff set PILOT in --dark-green
   #259271 and PRODUCTION in --blue #23649e. #259271 is 3.87:1 on this ground —
   the exact failure that had been raised and acted on an hour earlier, when the
   old panel lists were moved off it for being too faint — so it was darkened to
   a scoped #1b6d53 at 6.25:1, hand-matched to --blue's 6.20:1 so the two sides
   would not read as a hierarchy. That match was the whole reason for inventing
   a one-off hex, and one colour makes it unnecessary: --dark-teal is 9.28:1,
   which is better than either, and identical by construction. #1b6d53 is gone
   and should not come back.

   THE COLOUR WAS NEVER CARRYING THE ARGUMENT ANYWAY. What separates pilot from
   production here is that one ring is CLOSED and the other is OPEN — a
   protected world with an edge versus one without. Two label colours were a
   second, weaker copy of that distinction, and a reader who cannot see colour
   was getting it from the drawing regardless. The rings followed the words to
   the same teal an hour later, so the section now has no encoding colour at all
   and loses nothing by it.

   --dark-teal is also what the Extract → Enhance → Exploit arrows moved to
   earlier the same day, for the same reason: it is this page's colour for a
   mark that labels rather than accents. */
.hv4-rg-label {
    font-family: "brandon-grotesque", sans-serif;
    font-size: .875rem;
    font-weight: 700;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--dark-teal);
    /* Adds to .hv4-rg-col's .5rem gap to make ring→label 20px. See the comment
       on that rule — these two numbers are one decision in two places. */
    margin-top: .75rem
}

/* THE GAP COLUMN. Its height IS the ring, which is what lets the bar be
   positioned against the circles' centre line rather than against its own
   content — so the dimension line runs through both circles at any ring size.
   Everything inside is positioned off the same variable and nothing here is a
   literal pixel offset any more.

   *** THE STATEMENT OVERHANGS THIS COLUMN AND THE ROW DOES NOT GROW FOR IT. ***
   .hv4-rg-gap-copy is absolutely positioned, so it contributes nothing to the
   grid's height — the row is sized by the two caption columns instead. It was
   221px (180 ring + 20 gap + 21 label) against a 203px statement, i.e. 18px of
   headroom on a 29px line, so a fourth line would have hung into #hv4-cost.

   *** 09-21 THIS STOPPED BEING TIGHT: the four-line captions took the row to
   346px, so the statement now clears it by ~140px. *** The constraint is no
   longer live, but it is also no longer guarded by anything — it went away
   because the columns beside this one got taller, not because this column got
   safer. If the captions are ever shortened or dropped, the 18px is back.

   Either way, do not patch an overhang with a taller `height` here — that would
   push the bar off the circles' centre line, which is the one thing this column
   exists to prevent. Give .hv4-rg-gap a `min-height` instead and leave `height`
   alone, or shorten the sentence. */
.hv4-rg-gap {
    position: relative;
    height: var(--rg-ring)
}

/* The dimension line — now a plain rule, with nothing on its ends.

   *** THE END TICKS ARE GONE, 09-18: "let's remove the end bars of the orange
   line." *** They were two 23px uprights centred on the bar, drawn as ::before
   and ::after, and they came from the handoff, where they made the bar read as a
   measurement between two points rather than as a divider. Both pseudo-elements
   are deleted here AND in the ≤991 block, where they were the bar's horizontal
   end caps — if they are ever restored, both places need it.

   WHAT HOLDS THE MEANING WITHOUT THEM: the bar already ends exactly at the two
   circles, because it spans the middle grid track and the circles are the outer
   two. The ticks were marking ends that the drawing marks on its own, so losing
   them costs the reading nothing and drops two pseudo-elements.

   `top` is the ring's half-height less half the bar's own 3px, so the line sits
   on the circles' centre at any ring size. Do not replace this with a literal —
   it was 119px when the ring was 240 and that is exactly the kind of number
   that silently stops being true. */
.hv4-rg-bar {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(var(--rg-ring)/2 - 1.5px);
    height: 3px;
    background: var(--bright-orange)
}

/* *** SHORTFALL, CARRIED FORWARD KNOWINGLY: --bright-orange is 3.20:1 on this
   ground and 1.4.3 asks 4.5:1 for 16px text. It passes 1.4.11's 3:1 as a
   graphic, so the BAR is fine and only the LABEL is short. This is the same
   position the spend chart's hurdle label sits in and it was accepted there, so
   it is accepted here for the same reason and recorded rather than fixed.

   Label and bar take the SAME hex, per the standing rule that a label naming a
   coloured object is that object's colour. Do not darken the type alone — the
   two stop being one thing the moment they differ, and a 16px tracked cap does
   not read as the same colour as a 3px rule even when it is. *** */
.hv4-rg-gap-label {
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(50% + 20px);
    text-align: center;
    font-family: "brandon-grotesque", sans-serif;
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: .16em;
    text-transform: uppercase;
    line-height: 1.2;
    white-space: nowrap;
    color: var(--bright-orange)
}

/* 40ch, centred inside whatever the gap column turns out to be. The cap is
   doing real work: this column is the one that grows with the viewport, and an
   unbounded centred paragraph at 1440 would run past 80 characters a line.

   Label and copy are both placed off the column's CENTRE — 20px above the bar
   and 26px below it — rather than off its top, so the three stay a fixed
   distance apart when the ring resizes. As literals (60 / 146 against a 240
   ring) they drifted apart the moment the ring changed. */
.hv4-rg-gap-copy {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(50% + 26px);
    margin: 0 auto;
    max-width: 40ch;
    text-align: center;
    font-family: "leo", sans-serif;
    font-size: 1.25rem;
    line-height: 1.45;
    color: var(--dark-navy);
    text-wrap: pretty
}

.hv4-rg-gap-copy em {
    font-style: italic
}

/* *** THERE IS NO ONE-SENTENCE-PER-LINE RULE HERE ANY MORE, AND THAT IS A
   DECISION. BUILT 09-18, REMOVED 09-24. DO NOT REBUILD IT. ***

   It was `.hv4-rg-gap-copy span { display: block }` — spans rather than <br>
   precisely so it could be switched back to `inline` at 991, where forcing
   blocks turned three sentences into five ragged lines. It held for as long as
   the copy was three sentences that each happened to fit the column.

   The 09-23 Miro review cut the copy to two sentences and the first one no
   longer fits: "one sentence per line" then rendered as THREE lines with
   "readiness gap." stranded alone on the second. 09-24: "this paragraph looks
   off. we don't need to separate the sentences." The paragraph now wraps
   normally at every width, i.e. what the ≤991 case always did.

   The 18px-of-headroom note on .hv4-rg-gap is the thing to watch if this copy
   grows again — see that comment, and re-measure rather than assume. */

/* ══ STACKED, ≤991 ═══════════════════════════════════════════════════════════
   The handoff stops at 1200px desktop and says to confirm phone behaviour with
   design. Confirmed 09-18: stack pilot → gap → production, and TURN THE BAR
   UPRIGHT so it still measures the distance between the two circles. That is
   the only choice that keeps the drawing's meaning — a horizontal bar under a
   vertical stack would be measuring nothing, because on a phone the distance
   between pilot and production is vertical.

   Everything here is the same handful of values rotated: the column stops being
   a positioning context and becomes a flex stack, the bar swaps its width and
   height, and the ticks swap theirs.

   *** THE ONE 160px UPRIGHT IS GONE, 09-25, AND IT IS NOT COMING BACK AS ONE
   LINE. *** It was the handoff's suggested length and it survived until the
   layout was actually looked at on a phone: rotated, the bar stopped reading as
   a measurement and read as roughly an inch and a half of empty orange between
   the label and the sentence, with nothing beside it to measure against. A
   dimension line needs two things either side of it; stacked, both of those are
   circles hundreds of pixels away.

   "let's make it so the readiness gap has the copy underneath it directly. then
   put a smaller orange line above and below it." So the stacked block is now

       rule · THE READINESS GAP · copy · rule

   — the label and its sentence bound together as one unit, with a short rule top
   and bottom marking where that unit starts and stops. The rules still descend
   from the pilot circle and continue down to the production one, so the
   connector reading survives at a tenth of the emptiness.

   BOTH RULES ARE PSEUDO-ELEMENTS AND .hv4-rg-bar IS HIDDEN, deliberately. The
   real element is the desktop dimension line and only that; using it for one of
   the two would leave a matched pair maintained in two different places, which
   is exactly how they drift apart. They are declared together here and they stay
   declared together. 40px each — a quarter of what the single bar was, and short
   enough to read as a terminator rather than a span.

   The gap column's --rg-ring height goes — it existed only to line the bar up
   with the circles' centres, and there are no circles beside it any more. */
@media(max-width: 991px) {
    .hv4-rg {
        grid-template-columns: minmax(0, 1fr);
        gap: 2.5rem;
        margin-top: 3rem;
        justify-items: center
    }

    /* .75rem, not the 1.25rem it was: the label and the copy are one unit now
       and have to sit closer to each other than either sits to its rule. */
    .hv4-rg-gap {
        position: static;
        height: auto;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: .75rem
    }

    .hv4-rg-bar {
        display: none
    }

    .hv4-rg-gap::before,
    .hv4-rg-gap::after {
        content: "";
        width: 3px;
        height: 40px;
        background: var(--bright-orange)
    }

    /* `position: static` is what drops the desktop's centre-relative offsets;
       the two then fall into document order, which is already label → copy in
       the markup, so nothing needs reordering here. The ::before and ::after
       bracket them as the flex stack's first and last children. */
    .hv4-rg-gap-label,
    .hv4-rg-gap-copy {
        position: static;
        max-width: 40ch
    }

    /* The `display: inline` override that used to sit here is gone with the
       desktop rule it undid — see the 09-24 note above. This paragraph wraps
       the same way at every width now, so there is nothing left to switch. */

    /* *** THE CAPTIONS NEEDED THE SAME CAP AND DID NOT HAVE IT. *** On desktop
       each caption is the only child of a fixed 280px grid track, so it is 280px
       wide and sets in three centred lines — the whole `text-wrap: balance` note
       at line ~1500 is about getting those three lines even. At ≤991 that track
       is gone: the column is `minmax(0, 1fr)` and the caption stretches to the
       container, so it ran 560px ON ONE LINE under a 190px circle. Centred text
       three times wider than the thing it captions does not read as a caption.

       `balance` cannot save it, because one line is already balanced. The cap is
       what restores the geometry: 40ch at .9375rem measures 321.6px, within 42px
       of the desktop track and enough to put the caption back into three centred
       lines, which is the shape `balance` was tuned for. It is the
       same 40ch the gap label and copy above take, which is why they survived
       this collapse and the captions did not: the rule they are in was written
       for `position: static`, and the caption had no position to reset, so it
       was never added to the list and the max-width went with it.

       ⚠️ A SHARED DECLARATION IN A RULE NAMED AFTER A DIFFERENT PROBLEM IS HOW
       THIS HID. Nothing about "drop the centre-relative offsets" suggests the
       captions belong there. Kept as its own rule so the next element that needs
       a 40ch cap is not filed under whether it happens to be positioned. */
    .hv4 .hv4-rg-caption {
        max-width: 40ch
    }
}

@media(max-width: 767px) {
    .hv4-rg-gap-copy {
        font-size: 1.125rem
    }
}


/* ==========================================================================
   5. Cost — navy band. A stacked column chart of cost per developer on the
      left, the governance argument on the right. The chart's one job is to
      show cost pressure entering the system and the return obligation
      attached to it; captured value is deliberately absent, because there is
      no data for it, and the right column handles that.

      Everything the chart says has been cut from the copy, which is why the
      right column is three short beats rather than the four paragraphs it
      used to be.
   ========================================================================== */

/* Chart-local colours. The handoff's token table was built for a navy surface
   and every text value in it was a light tint (--spend-body was #d4dadc), so
   moving the section to a light ground inverted all of them.

   The tiers are much closer together than they were on navy, and that is not
   an accident of picking: a light ground has far less room underneath it than
   a dark ground has above it. Against #f4f6f6, 4.5:1 needs a luminance under
   about .162, which puts the whole legible range into a narrow band. So the
   hierarchy here is carried by size and weight, with colour only confirming
   it — 12px tracked caps against 17px serif reads as secondary whether or not
   the two are three shades apart.

   --spend-muted 5.6:1, --spend-faint 4.6:1, both on #f4f6f6. There is no
   third, fainter tier available; anything lighter fails.

   --spend-slate (#4e7c89) is gone. It was the developer bar's fill, the one
   value that had carried over from the navy handoff unchanged because a fill
   answers to the 3:1 graphical minimum rather than 4.5. The bar is a hollow
   outline now, so there is no fill to hold a contrast ratio — #fcfcfc inside a
   1px #d5dde0 border, both declared where they are used, because they are the
   material of one component and not a tier of anything. */
#hv4-cost {
    --spend-grid: rgba(6, 41, 53, .14);
    --spend-body: var(--text-muted);
    --spend-muted: var(--text-muted);
    --spend-faint: #56737d
}

/* Above the grid and on the section's own 66.67% measure, which is where it
   started and where 09-17 puts it back: "for the spend compounds and its copy
   underneath this should spread across the 65% of the page like the other
   sections do."

   For one pass it lived in the chart track with max-width: none, to close a
   110px hole the governance column had under it. That hole is closed a different
   way now — the grid is one row, so the governance column starts at the card's
   top edge and there is nothing above it to clear. Nothing declared here any
   more; size, colour, measure and margin all come from .hv4-lead. */

/* Chart and copy are peers in a single row, and the row starts at the top of
   both: "let's have this section line up with the graphic."

   FLIPPED AND PUT BACK, both on 09-17. "what if we flipped this" moved the copy
   left and the chart right; "let's put this back" returned it. CHART LEFT, COPY
   RIGHT is the settled arrangement — do not re-offer the flip.

   The flip was built with `order: -1` on .hv4-gov rather than by moving the
   markup, and it was reset at the one-column collapse so a stacked read still went
   chart-then-copy. Both of those rules are gone: with the chart back on the left
   the visual order and the source order agree at every width, which is one fewer
   thing that can drift. Source order is chart then governance — the order the
   argument is built in, and the order a screen reader gets.

   The chart keeps the wider track, 1.15fr, because five slots need the room and
   the copy column is three short beats.

   THE PHOTOGRAPH MOVES WITH THIS. #hv4-cost::before is masked so it never runs
   under the chart card, so it sits on whichever side the prose is — bottom-right
   again. Three values there turn over together: the anchor, the
   background-position, and the first mask ramp. See that rule.

   IT COLLAPSES TO ONE COLUMN AT 991, which is the rung every other two-column
   layout on the page turns at — see the note above that media query for the full
   audit and why this rule moved off 1199.

   The "reset grid-area on all three children" warning that used to sit here is
   gone with the layout it described: this grid has two children, .hv4-spend and
   .hv4-gov, and neither is placed, so the collapse is a track change only. If a
   third child or an explicit placement ever comes back, put the warning back
   with it. */
.hv4-cost-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: 0 5.5rem;
    align-items: start
}

/* 260px rather than the 432px this started at. The plot is the track less both
   gutters, so its width runs 563px at the 1720 container down to 415px at the
   1440 two-column worst case; 260px keeps the ratio between 2.2:1 and 1.6:1
   across that range instead of the 1.3:1 it was heading for.
   --spend-key-gutter is the strip on the right holding the direct labels. */
/* The figure IS the card — title, plot and year axis inside one border. It was
   a separate .hv4-spend-card wrapping only the plot, with the title outside on
   the page ground; that read as two objects, which is not what this is.

   The card reverses the handoff, which is explicit — "no card, no border, no
   shadow — it is part of the page, not a widget" — and the reversal is a client
   call, so it is worth writing down what it costs and what it buys. It costs
   the chart's claim to be page content rather than a panel. It buys a defined
   surface, which matters more than it did: the section ground is getting a
   photographic texture, and a chart drawn straight onto a photograph cannot
   hold its gridlines.

   1px #e3e7e9, the page hairline — the value that reads on white and disappears
   on the light grounds, the opposite of the rgba navy the gridlines use. Kept
   lighter than the #d5dde0 on the bars inside it: the frame of the chart should
   not be heavier than the data in it.

   4px radius and the two-layer shadow the readiness-gap card and the delivery
   aside already use. One tight 1px layer to seat the edge, one 18px layer at 8%
   to lift it off the ground — that is the page's existing "slight shadow" and
   there was no reason to invent a second one. The square corners the handoff
   asks for are still square everywhere they are data: bars, gridlines, ticks.
   This is the container, not the graphic.

   --spend-gutter is 0 and that is a consequence, not a preference. It was 3.5rem
   to hold the $220K / $200K / $180K / $170K ticks hanging off the left of the
   gridlines. Those ticks were removed by instruction, so the gutter has nothing
   to hold and 56px goes back to the plot — the one change on this card that
   makes the bars wider rather than narrower. If dollar ticks ever come back,
   this is the value to restore, and 24px of card padding is what leaves the
   widest of them 3px clear of the border.

   The right gutter stays: the series labels genuinely use all of it. 116px as
   of 09-17, up from 100px, which is the 10px swatch plus its 6px gap — the text
   column is unchanged at 88px, so no label wraps differently than it did. The
   16px comes off the plot; nothing else in the card is a candidate, since the
   left gutter is already 0.

   NO TOP PADDING, and `overflow: hidden`. Both exist for the title band: the
   band is full-bleed, so it has to start at the border and the card's own top
   padding would hold it off; and it has square corners of its own, so the card
   has to clip them to its 4px radius. The band carries the 1.5rem back as its
   own padding, so nothing above the plot moved. */
.hv4-spend {
    --spend-gutter: 0px;
    --spend-key-gutter: 7.25rem;
    --spend-plot-h: 260px;
    margin: 0;
    background: var(--white);
    border: 1px solid #e3e7e9;
    border-radius: 4px;
    box-shadow: 0 1px 2px rgba(6, 41, 53, .06), 0 6px 18px rgba(6, 41, 53, .08);
    padding: 0 1.5rem 1.5rem;
    overflow: hidden
}

/* Brandon tracked caps — the section-label pattern from the case-study pages
   ("THE SITUATION"), minus its hairline. The rule came with the pattern and was
   removed on sight: inside a card there is already a border carrying the eye
   across the block, so a second horizontal line 25px below the first one was the
   same job done twice. On the page ground, where nothing else draws that width,
   the rule is what lets a 12px label title something; inside a container it is
   redundant with the container.

   It went round twice before this. It was leo at the governance heading's size
   for one pass, on the argument that the chart column and the copy column
   should be titled alike; inside a card that made the card look like it had a
   section heading stuck to its ceiling. Brandon caps is the right voice for the
   title of a graphic — it is the same voice as the ticks, the years and the
   series labels, so the whole piece speaks in one register and the two leo
   italics ("Hurdle Rate", "AI Arrives") stay the only annotations.

   A FULL-BLEED HEADER BAND as of 09-17, against a supplied reference: a white
   card with a coloured strip across its whole top edge carrying white tracked
   caps. It was navy type on the card's own white before that. The strip is
   --dark-teal because the instruction was "but not yellow do like the teal
   color", and because teal is the one brand colour with no job inside this
   chart — green and blue are both encoding a data series, and orange belongs to
   the section above. A band in either series colour would read as a key.

   The bleed is negative side margins of exactly the card's padding, paid back
   as the band's own padding, so the type still starts on the card's text edge
   and the plot below it has not moved. The card carries `overflow: hidden` so
   the band's two top corners take the card's 4px radius; the card has no top
   padding, so the band starts at the border.

   White on #0e4d64 is 9.28:1, which clears AAA at any size — this is a small
   tracked cap and it is the one place the ratio should not be close.

   1.75rem under the band, which is the air the title used to carry as its own
   bottom margin. The value did not change; what changed is that it is now
   measuring from a coloured edge rather than from a line of type. */
/* Centred 09-17: "center the bar garph title". The band bleeds the full width of
   the card, so left-aligned type inside it sat at the card's text edge and read
   as the first line of the card's content; centred, the band reads as the
   card's own header. Note the .18em of tracking is applied to the last letter
   too, so a truly centred string sits about 1px left of optical centre — that is
   below the threshold worth a negative margin-right to correct. */
.hv4-spend-title {
    margin: 0 -1.5rem 1.75rem;
    padding: .875rem 1.5rem;
    background: var(--dark-teal);
    font-family: "brandon-grotesque", sans-serif;
    /* 13px. The header band sits one step ABOVE the card's 12px floor because
       it is the only label of the four that names the whole figure rather than
       a part of it. White on #0e4d64 is 9.28:1, so the size is the only thing
       that was ever limiting here. */
    font-size: .8125rem;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    text-align: center;
    color: var(--white)
}

/* The hurdle: a 2px line inside the plot at the $226K mark, spanning exactly
   the plot and not the gutters — the same insets as a gridline, because it is
   the same kind of object, a value drawn across the scale.

   It used to be a block above the frame with the line on its underside, on the
   argument that a rule between the gridlines would be read as a dollar level.
   Anchoring it is better: it *is* a dollar level, $226K, and it now has a navy
   tick in the gutter saying so. A threshold that floats above the scale is a
   decoration; one tied to a number is a claim the bars can be measured against,
   which is the whole point of drawing it above them.

   --bright-orange (#f46036), and it has been three colours: --dark-green, then
   --bright-blue for a pass, now orange. Green went because green meant a data
   series in this chart, so the judgement could not be green too. Blue has gone
   for exactly the same reason as of 09-17 — the AI segment is --blue now, and a
   threshold drawn in the colour of one of the two series it judges reads as
   belonging to that series.

   Orange is the only accent left with no encoding job in the card, and it is
   the page's threshold colour already: the readiness-gap total is the same hex.
   It measures 3.09:1 on the white card, which clears the 3:1 WCAG 1.4.11 asks of
   a graphical object that carries meaning. The label takes the same hex, per the
   hurdle-pair rule; at 14px that is short of the 4.5:1 for type and is the same
   shortfall already accepted on the readiness-gap label. FLAGGED, not fixed.

   --bright-yellow was tried once and reverted on sight: 1.77:1 on the white
   card, the faintest thing in the chart while being the thing the bars are
   measured against. Do not re-offer it. */
/* ONE DASHED CURVE, asked for on 09-16: "make the hurdle rate dashed an maybe
   more of a curved line or something more interesting thatn stairs".

   It was nine absolutely-positioned spans — five steps and four risers — which
   drew a staircase. The staircase was literally true (the hurdle is a figure per
   year, not a continuous function) and it read as a series in its own right,
   competing with the bars it is supposed to judge. One curve through the five
   year-centre points reads as a threshold rising, which is the claim.

   Dashed for the same reason the AI-arrives divider is: on this chart a broken
   stroke means modelled rather than measured. The bars are the measured series;
   this line is 8% of each year's total, computed. It is the only dashed thing
   inside the plot.

   The SVG stretches to a non-square plot, so BOTH of these are required and
   neither works without the other:
     preserveAspectRatio="none"      — makes the 0-100 viewBox land the points on
                                       exact percentages of a 383px-tall,
                                       ~700px-wide plot
     vector-effect="non-scaling-stroke" — stops that same non-uniform stretch
                                       turning a 2px dash into a wide horizontal
                                       smear. Remove it and the dashes flatten
                                       into lozenges.
   overflow: visible so the half-stroke at y=9.5875 is not clipped by the top
   edge of the box. */
.hv4-spend-hurdle {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    pointer-events: none
}

.hv4-spend-hurdle path {
    fill: none;
    stroke: var(--bright-orange);
    stroke-width: 2;
    stroke-dasharray: 6 5;
    stroke-linecap: round;
    stroke-linejoin: round
}

/* Leo italic, navy, Title Case — the annotation voice, matching "AI Arrives".
   It was 12px tracked brandon caps, which is this page's label voice and was
   right while the hurdle was a titled block sitting above the chart. Inside the
   plot it is an annotation on a line, so it takes the serif.

   Sits 4px above its own line, at the plot's left edge. The 6.7% of scale left
   above $226K is 17px, so the label overhangs the frame by about 7px into the
   eyebrow's 28px margin. Nothing is clipped and nothing collides: the tallest
   bar tops out at 90.6%.

   The note that used to run beside it — "8 to 12% on the AI spend, before any
   of it counts as return" — is gone. The revision's label is two words, and the
   percentage it carried is in the disclosure under the chart, which already had
   to state that the hurdle is modelled. */
/* --bright-blue, matching its own line rather than the navy it was. An
   annotation that names a rule should be the same colour as the rule — that is
   what ties the two words to the 2px line 4px under them, and it is why
   "AI Arrives" stays navy: that one names a moment on the axis, not a coloured
   object.

   FLAGGED: #3499e7 on white is 3.06:1. That clears the 3:1 for the line but not
   the 4.5:1 WCAG asks of 14px text, so these two words are the one piece of type
   in the card that does not meet AA. Deliberate, on the rule that the accent
   colour is not darkened to buy a ratio. The compliant version of the same idea
   is --blue (#23649e) on both the line and the label, which measures 5.6:1 and
   still ties them together; #2e8fd9 sits in between at 3.6:1 on white. */
/* --bright-blue, the exact value of its own line, and 500 rather than the 400
   this inherited.

   Same hex on both is the requirement, stated twice and stated last: these two
   words and the 2px rule 4px under them are one object and must not be two
   blues. Do not "improve" this by darkening the type.

   --blue (#23649e) was tried for one pass, borrowed from the readiness-gap
   graphic's labels, where the rule took an accent and the type one step darker.
   (Those rules are gone — that graphic went back to its row version on 09-16 —
   but the argument is the thing being recorded.) That pattern is real and it
   does not apply here: those labels were 11px brandon bold caps, a dense block
   of ink that can afford a darker value; this is a 14px italic serif whose whole
   character is that it is light. Rejected on sight — "not same blue".

   Weight 500 stays. It is the one lever that closes the perceived gap without
   touching the value: a 14px italic serif is thin strokes and heavy
   antialiasing, so half of every stem blends towards the white card while a
   solid rule loses nothing, and 500 puts some of that ink back. It is a real leo
   italic face — the advance width changes, where 600 and 700 leave it alone, so
   those two are synthesised and would thicken unevenly.

   FLAGGED, and worse than the blue it replaced. --bright-orange (#f46036) on the
   white card is about 3.1:1 — fine for the 2px rule under WCAG 1.4.11's 3:1 for
   meaningful graphics, short of the 4.5 AA wants for 14px type, and these two
   words are the only type in the card that does not meet AA. The orange was
   asked for on both the line and the label together, so both have it.

   Note that matching the rule's hex does NOT make the label read as the same
   colour, and that is a property of the type rather than a mistake: a 14px leo
   italic is thin strokes and heavy antialiasing, so half of every stem blends
   toward the white card while a solid 2px rule loses nothing. Weight 500 is the
   one lever that puts ink back without changing the value. It is a real leo
   italic face; 600 and 700 are synthesised and thicken unevenly. */
.hv4-spend-hurdle-label {
    position: absolute;
    right: 0;
    margin-bottom: 4px;
    font-family: "leo", serif;
    font-style: italic;
    font-weight: 500;
    font-size: .875rem;
    line-height: 1.4;
    color: var(--bright-orange)
}

/* Frame holds the axis gutter, the plot and the label strip in one coordinate
   space, so the gridlines can span the plot while the axis labels hang back
   into the left gutter and the segment labels into the right. Absolute children
   resolve against the padding box, so they start at var(--spend-gutter). */
.hv4-spend-frame {
    position: relative;
    height: var(--spend-plot-h);
    padding-left: var(--spend-gutter);
    padding-right: var(--spend-key-gutter)
}

/* The axis rule, and the reason there is no gridline at the $170K floor: navy
   at 28% is twice the .14 the gridlines use, so it closes the plot rather than
   being the lowest line in it. The scale has no line of its own at the bottom
   because this is it.

   It belongs to the frame, inset to exactly the plot, and not to the year
   labels below — which is where it was first put. A border-top on
   .hv4-spend-years spans that element's padding as well as its content, so the
   rule ran the full width of the figure and out under both gutters: past the
   dollar ticks on the left and the series names on the right. An axis that
   extends beyond the data it bounds stops reading as an axis and starts reading
   as a divider between the chart and its caption. */
.hv4-spend-frame::after {
    content: "";
    position: absolute;
    left: var(--spend-gutter);
    right: var(--spend-key-gutter);
    bottom: 0;
    height: 1px;
    background: rgba(6, 41, 53, .28)
}

/* Solid hairlines one step off the surface. Never dashed: on this chart a broken
   stroke means modelled rather than measured, and the hurdle curve is the only
   thing that is. (That reservation used to belong to the AI-arrives divider,
   which went solid on 09-16.) */
.hv4-spend-rule {
    position: absolute;
    left: var(--spend-gutter);
    right: var(--spend-key-gutter);
    height: 1px;
    background: var(--spend-grid)
}

/* There were two more rules here, `.hv4-spend-rule--bare` and
   `.hv4-spend-rule span`, which drew the $220K / $200K / $180K / $170K ticks in
   the left gutter. Both are gone with the ticks, and so is --spend-faint's last
   reader. */

.hv4-spend-plot {
    position: relative;
    height: 100%
}

/* Quiet again: 1px of navy at 25%, full plot height. This was 2px dashed
   --dark-green, which was a deliberate step up from the original handoff's
   #1E4553 on the grounds that it is the one line worth emphasising. The chart
   revision takes it back down, and the reason holds: the hurdle is now the
   emphatic line, in blue, and two emphatic lines crossing each other is a
   graphic with two protagonists. What this marker has to say is only "the data
   changes character here", which a faint dash says on its own.

   Drawn with a repeating gradient rather than `border-left: dashed`, because
   the dash length is the point — 3px on, 6px off. `dashed` lets the browser
   pick, and at 1px Chrome draws roughly 2px on, 2px off, which is close enough
   to a dotted line to lose the distinction from the solid gridlines it crosses.

   Full height, top to bottom, now that the label is beside it rather than above
   it. It used to start 1.625rem down to clear its own label.

   40%, not the revision's 39.4%. That figure comes from five 15%-wide columns
   distributed with space-between; these are five equal flex slots with the bar
   centred in each, so the midpoint of the gap between slots two and three is
   exactly 2/5 of the plot. Same intent, different geometry — and the revision
   asks for it to be recomputed rather than copied.

   SOLID AND BLUE as of 09-16: "AI arrives make a solid line that is the bright
   blue". Everything above about keeping it quiet is the reversed argument and is
   left standing so it does not get re-proposed.

   What makes the two lines still separable now that both are coloured is that
   they are separable on FORM rather than on weight: this one is solid and
   vertical, the hurdle is dashed and horizontal. That is also the better split,
   because it now matches what the chart means by a dash — modelled rather than
   measured. A solid line for the quarter the tokens actually started is correct;
   the hurdle is computed, so it is the one that should have been broken.

   Dashed was the only marker in the plot using a broken stroke, which is why the
   note on the gridlines above still says dashed is reserved for this divider.
   That reservation has moved to the hurdle.

   2px as of 09-17: "AI Arrives line needs to be thicker to be consistent with
   site". That is the page's own standing rule rather than a one-off — a
   coloured rule that divides is 2px, a 1px hairline is neutral and connects
   beats. At 1px this one was doing a divider's job at a hairline's weight, and
   it now matches the hurdle's 2px stroke. Correct, because the two are the same
   class of object: they are separated by form, not by weight.

   translateX(-1px) because the position is load-bearing. 40% is the exact
   midpoint of the gap between slots two and three, and an unshifted 2px line
   sits entirely to the right of that point.

   GREY as of 09-17: "let's make AI arrives a grey", in the same instruction
   that took the AI-spend bars to --bright-blue. The blue did not fail here; it
   was reassigned. It is the encoding colour for a data series now, and a
   divider that shares a series' hex claims to be part of that series. This line
   marks a moment on the time axis and belongs to the apparatus, with the
   gridlines and the tick labels, which is what --spend-muted already is.

   That restores the state before 09-16, and the argument for the blue is
   preserved in the block above so it does not get re-proposed. It also frees
   the two lines to be told apart by colour again — grey annotation, orange
   hurdle — on top of the solid-vertical against dashed-horizontal split, which
   stays.

   --spend-muted is 6.0:1 on the white card, so a 2px rule clears 1.4.11's 3:1
   with room and the 14px label beside it clears AA. Both of the blue's flagged
   shortfalls go with it.

   THE RULE IS 90% OF THE LABEL'S VALUE, 09-17: "these need to be the same
   color", pointing at this line and the words beside it. Measured, they already
   were — both computed rgb(74,102,112) — so this is a rendering difference, not
   a declaration one, and it is the same one the hurdle pair has on record: a 2px
   solid rule lays down its value at full strength, while a 14px italic serif
   spends half of every stem on antialiasing towards a white card and comes out
   paler. Same hex, two apparent colours.

   The fix goes on the RULE, not on the type. The standing rule for this chart's
   annotation pairs is that the type is never darkened to compensate (see
   memory/homepage_v4_cost_chart.md — --blue on "Hurdle Rate" was applied on
   exactly that argument and rejected on sight), and the label's whole character
   is being light. So the rule gives up the 10% instead. Hue and the declared
   value are untouched; only the alpha moves, and the pair now reads as one
   colour, which is the entire point of the one-hex rule.

   .9 not lower: at 2px over white, alpha .9 computes to about 5.4:1, still well
   clear of 1.4.11's 3:1 for a divider, and the line does not soften enough to be
   mistaken for a gridline (those are rgba(6,41,53,.14)). */
/* SHORTENED 09-17: "can you shorten this line a little bit and then have AI
   Arrives centered above it." It ran the full height of the plot; it now starts
   at 14% down, which leaves room for the label directly over its head instead of
   hanging off its left side. The two values are a pair — the label's `bottom` is
   this `top` read from the other end (100 - 14 = 86%), so if one moves the other
   has to move with it. */
.hv4-spend-arrive {
    position: absolute;
    left: 40%;
    top: 14%;
    bottom: 0;
    width: 2px;
    transform: translateX(-1px);
    background: color-mix(in srgb, var(--spend-muted) 90%, transparent)
}

/* Leo italic to the left of the line, matching "Hurdle Rate": the two
   annotations in this chart share a voice, and it is not the label voice the
   ticks and series names use. It was 12px tracked brandon caps centred over the
   line, which read as a column heading for the two years to its right.

   CENTRED OVER THE LINE'S HEAD as of 09-17 ("have AI Arrives centered above
   it"). It hung off the line's left side before that, at bottom: 92%, having
   gone there from 73% on "move AI Arrives higher up". It now sits at 86%, which
   is the line's own new top edge, plus .35rem of air — so the height is no
   longer a free choice: it is wherever .hv4-spend-arrive starts.

   That also settles the gridline problem the old heights were picked around.
   The gridlines are at 16.7, 50 and 83.3; the label's box is about 8% of the
   plot tall, so sitting ON TOP of a line that starts at 86% puts it clear of
   83.3 by construction rather than by choosing a number between the lines.

   Still needs no knocked-out background, which is what the revision specified
   and what clearing the gridlines buys instead.

   --text-muted, not the revision's #7B939B. That value measures 3.0:1 on the
   white card and this is 14px body-size text, so it needs 4.5; --text-muted is
   6.0:1 on white. The recession the lighter grey was for comes from the serif
   against the tracked caps instead, which is the type rule the revision states
   for itself.

   --spend-muted, back where it was. It went --bright-blue for one day on 09-16,
   following its line, and comes back with it on 09-17 ("let's make AI arrives a
   grey"). Both moves are the same rule applied twice: an annotation and the
   coloured object it names take the exact same hex. The line is grey, so this
   is grey.

   That also settles the open question from the blue pass, where only the LINE
   had been named and the label was moved on inference. It never needed
   answering — the instruction that would have confirmed it reversed it instead.

   Weight 500 for the same reason the hurdle label carries it: a 14px italic
   serif is thin strokes and heavy antialiasing, so at one hex it still reads
   lighter than its own rule. 500 is a real leo face; 600 and 700 are synthesised.

   The blue's contrast shortfall goes with the blue. --spend-muted is 6.0:1 on
   the white card, which clears AA for 14px; #3499e7 was 3.06:1 and did not. */
.hv4-spend-arrive-label {
    position: absolute;
    left: 40%;
    bottom: 86%;
    margin-bottom: .35rem;
    transform: translateX(-50%);
    white-space: nowrap;
    font-family: "leo", serif;
    font-style: italic;
    font-weight: 500;
    font-size: .875rem;
    line-height: 1.4;
    color: var(--spend-muted)
}

.hv4-spend-cols {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: flex-end
}

/* One slot per year, columns centred in the slot with the rest left as air.

   No gap between the two segments. There used to be 2px of surface showing
   through, which was the right way to separate two solid fills. The developer
   bar is now a hollow outline, so the AI segment sits directly on its top
   border and the two read as one stack with the green added to it — which is
   the sentence the chart is making. A gap plus a border would have put three
   lines between the segments. */
.hv4-spend-col {
    flex: 1;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end
}

/* 58px is the handoff's width; the percentage is what the bar falls back to once
   a slot is narrower than that. It has to be read against the label inside the
   green: "$18,000" is 41px at brandon 700 11px with .04em of tracking, so a bar
   under about 53px has the figure crowding both edges even though nothing clips.

   80%, not the 64% it was. The card took 50px off the plot, which put the 1440
   two-column slot at 70px and 64% of it at 45px — a bar with 2px of air either
   side of its own number. At 80% the same slot draws a 56px bar with 7px either
   side, and the gap between neighbouring bars is still 14px. The cap was set at
   64% when the plot was 415px wide and 58px was reached long before the
   percentage mattered; on a narrower plot the percentage is what is in force,
   and it was set too low to protect the thing it exists to protect. */
.hv4-spend-salary,
.hv4-spend-ai {
    flex: none;
    width: min(58px, 80%)
}

/* Solid --light-green (#4abd92) as of 09-17: "let's make developer cost our
   bright green 4abd92". This bar has now been four things: solid --spend-slate
   with a zigzag foot, a hollow white outline, solid --dark-green, and now the
   lighter green.

   FLAGGED, not fixed: #4abd92 measures 2.37:1 on the white card, against the
   3:1 WCAG 1.4.11 asks of a graphical object that carries meaning. #259271 was
   3.87:1 and cleared it, so this is a real regression and it is the edge of the
   bar against the card, not the boundary between the two series — that one
   improved (see .hv4-spend-ai). The fix if it is ever wanted is a 1px border in
   the darker green, which costs no fill colour. Darkening #4abd92 to buy the
   ratio is the thing we do not do.

   The hollow version was the handoff's, and its argument was that the developer
   salary is the given and the AI spend is the news, so the green should be the
   only ink in the chart. It broke when the card went white: white fill on a
   white card left a 1px outline doing the whole drawing, and the two pre-AI
   years read as empty space rather than as the baseline the green is stacked
   on. The chart's sentence is "$18,000 on top of a salary that was already
   climbing" and the salary was invisible.

   Two solid greens says it instead, and the border comes off — a fill does not
   need an outline, and a 1px light border round a mid-green bar would just be a
   pale halo. box-sizing goes with it: without a border there is nothing to pull
   inside the height, so the bar height is the data height exactly.

   The zigzag foot could come back now, since a solid fill can be torn and this
   axis still starts at $170,000 with nothing on the page saying so. Not
   reinstated without a decision — it is an editorial call, not a styling one. */
.hv4-spend-salary {
    background: var(--light-green)
}

/* --light-green (#4abd92), stacked on --dark-green.

   The two greens are the whole encoding: the darker one is the salary
   underneath, the lighter one is what AI added. Lighter on top is the right way
   round — the increment is the news, and it is the value that lifts off the
   card. It was #1F7E64 when the bar below it was a hollow outline, then
   --bright-green (#6ce0b5) for one pass.

   The type that used to be in here is gone: "remove the money amounts from the
   graph" (09-16). Flex centring, brandon 11px bold caps and white all came out
   with the figures, because nothing renders inside this bar any more.

   That removal also closed the worst contrast failure on the page — white on
   --light-green measured 2.34:1 against the 4.5 WCAG wants of 11px bold. It is
   not fixed, it is deleted; if a figure ever goes back inside this bar it comes
   back with the failure, and the compliant version is navy on the same green
   (6.53:1). Not veiled: darkening the brand green to buy a ratio is the thing we
   do not do.

   BLUE as of 09-17. Two instructions, hours apart: "let's make AI spend bright
   blue to help with contrast", then "let's make developer cost our bright green
   4abd92 and the AI spend our darker blue". So this was #3499e7 for one pass and
   is #23649e now.

   The whole point of going blue was separating the two segments, and the
   sequence of measurements is worth keeping because it is counter-intuitive:

   - #259271 under #4abd92, two greens .............. 1.66:1
   - #259271 under #3499e7, dark green + bright blue  1.26:1
   - #4abd92 under #23649e, light green + blue ...... 2.44:1

   The middle row is the one that surprises. Bright blue made the boundary
   obvious on screen while making the number worse, because WCAG contrast is a
   pure luminance ratio and #3499e7 and #259271 are close in luminance and far
   apart in hue. The current pair is better on both counts at once — it keeps the
   hue separation and puts a real value step under it, the light green being the
   brighter of the two by a clear margin.

   That also retires the greyscale flag that stood against the bright-blue pass.
   At 2.44:1 the segments still read as two blocks with luminance alone, so a
   printed or monochrome copy of the chart holds. The 1px white hairline that was
   noted as the fix is not needed and should not be built.

   Blue against green is also the safer hue axis: it survives both common forms
   of colour blindness, which red against green does not.

   No clash with the hurdle: that line and its label are --bright-orange, not
   the --bright-blue the comment lower down still claims. Blue is this series
   and nothing else in the card. */
.hv4-spend-ai {
    background: var(--blue)
}

/* Direct labels in the right-hand strip, replacing the key that used to sit
   under the chart. Centred on the mid-height of the year-five column's two
   segments, so each one reads as naming the block beside it.

   Flex as of 09-17, to carry the swatch below. align-items: baseline and not
   center, because "DEVELOPER COST" wraps to two lines and "AI SPEND" does not:
   centring would put one swatch beside the text and the other beside the gap
   between its two lines. A flex item with no baseline of its own takes its
   bottom margin edge as its baseline, so an empty square sits ON the first
   line's baseline, which is where a legend chip belongs. */
.hv4-spend-key {
    position: absolute;
    left: calc(100% + .75rem);
    display: flex;
    align-items: baseline;
    gap: .4rem;
    width: var(--spend-key-w, 6.5rem);
    transform: translateY(50%);
    font-family: "brandon-grotesque", sans-serif;
    /* 12px, the card's floor — see .hv4-spend-years span. Was 11px. */
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .14em;
    line-height: 1.35;
    text-transform: uppercase
}

/* THE SWATCH. "AI spend and developer cost should get a key to the left of
   their words with a coloured box" (09-17).

   This is the one thing that can carry the encoding without breaking the rule
   below it. The note on the type colour still stands — these two labels stay
   one muted grey, because a coloured word is a second green object competing
   with the bars. A chip is not a second object: it is a sample of the first
   one, which is what a legend has always been. So the colour moves from the
   type, where it was tried and reverted, to a 10px square, where it says the
   same thing and stops.

   10px is cap height plus a little at brandon 11px, and 2px of radius matches
   the card. flex: 0 0 auto so it cannot be squeezed by the wrapping label. */
.hv4-spend-key::before {
    content: "";
    flex: 0 0 auto;
    width: .625rem;
    height: .625rem;
    border-radius: 2px
}

.hv4-spend-key--ai::before {
    background: var(--blue)
}

.hv4-spend-key--dev::before {
    background: var(--light-green)
}

/* Both muted, the same colour. "AI Spend" used to be --dark-green, on the
   argument that a direct label should carry its series' colour. The revision
   makes them a matched pair and it is right: these two are one apparatus for
   reading the chart, and colouring one of them makes it a second green thing
   competing with the bars. The green already identifies itself — the label only
   has to name it. The swatch above is where that colour now lives.

   HEIGHTS CORRECTED 09-17: "I feel like AI spend should line up with the last
   green box". It did not, and the reason is worth recording because it is the
   kind of break that hides. These two percentages were computed against the old
   $170K-$230K plot — a 60,000 span. The scale was later widened to $170K-$250K,
   an 80,000 span, to fit the year-five hurdle, and every height in the MARKUP
   was recomputed at that point. These two live in the CSS and were missed, so
   both labels floated about 18 points of plot above the segments they name.

   Recomputed on the 80,000 span, from the year-five column as it is drawn:
   developer cost 0-40.75%, AI spend 40.75-67.975%. Midpoints 20.375 and
   54.3625.

   If the scale moves again, these two move with it. */
.hv4-spend-key--ai {
    bottom: 54.36%
}

.hv4-spend-key--dev {
    bottom: 20.38%
}

.hv4-spend-key--ai,
.hv4-spend-key--dev {
    color: var(--spend-muted)
}

/* Years line up with the plot columns, so they take both gutters. The note is
   prose and only needs its left edge to agree with the plot. */
.hv4-spend-years {
    display: flex;
    padding-top: .625rem;
    padding-left: var(--spend-gutter);
    padding-right: var(--spend-key-gutter)
}

/* *** 12px IS THE FLOOR FOR EVERY LABEL IN THIS CARD AND NOTHING GOES UNDER IT.
   *** Jess, 09-30, asked for the chart apparatus to come up; this row, the axis
   title, the legend and the header band are all on that floor now.

   This was 10px, and the reasoning for 10px was compositional rather than
   legible: five ticks reading "01" carry far less width than five reading
   "YEAR 1", so the row stopped needing size to hold the columns apart, and the
   axis stopped being the loudest thing under the plot. Both of those are still
   true and neither survives contact with a phone — 10px of tracked caps in a
   muted grey is the hardest thing on the page to read, and it was 10px at 1440
   as well, so this was never a mobile-only problem.

   The quiet the smaller size bought is kept by the OTHER two levers instead,
   which cost nothing in legibility: the colour stays --spend-muted rather than
   going to body ink, and the numerals stay bare ordinals rather than going back
   to "YEAR 1". Tracking stays at .14em.

   ⚠️ Do not re-derive this from how the row looks next to the plot. It looks
   slightly heavier at 12px and that is the trade being made deliberately. */
.hv4-spend-years span {
    flex: 1;
    text-align: center;
    font-family: "brandon-grotesque", sans-serif;
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .14em;
    color: var(--spend-muted)
}

/* The axis title — the noun the numerals dropped, said once. Centred on the
   plot rather than the card, so it takes both gutters like the tick row does:
   an axis label belongs to the axis, not to the container.

   .5rem above it, which is less than the .625rem between the plot and the
   ticks. The numerals and this label are one object read together, so they sit
   closer to each other than the pair sits to the bars. Costs ~13px of section
   height against Tim's density note; the 2px off every tick pays most of it
   back and the axis reads quieter overall. */
.hv4-spend-axis {
    padding-top: .5rem;
    padding-left: var(--spend-gutter);
    padding-right: var(--spend-key-gutter);
    text-align: center;
    font-family: "brandon-grotesque", sans-serif;
    /* 12px, the card's floor — see .hv4-spend-years span. It tracks the tick
       row it labels, so these two sizes move together or not at all. */
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--spend-muted)
}

/* The modelling disclosure that lived here is deleted along with the .hv4-spend-note
   element: "let's ditch the italic copy under the graph" (09-16). The full list of
   what it disclosed is recorded in the template at the point of deletion.

   --spend-faint (#56737d) is now read by nothing again. Left declared in the token
   block because it is the section's documented second tier, not because it is used. */


/* Right column — the governance argument the chart cannot draw.

   No padding-top, and there is no longer anything to align it to. It was .5rem,
   nudging this h3 down to sit level with the chart's 12px caps label; then the
   label became an h3 and the nudge was removed because two identical headings
   align themselves. The label is a 12px figcaption again, but it is now *inside*
   a bordered card, so what the grid has to line up is this heading's box against
   the card's top edge — which `align-items: start` does, and which is the right
   join: the card is one object and its ceiling is its top, not the text in it. */

/* The governance column head. Face and weight come from `.hv4 h1..h4` — leo
   500 — which is why neither is declared here; only the size, the leading and
   the space under it.

   The chart's title was in this selector for one pass, on the argument that the
   two columns should be titled alike. It came out when the chart became one
   bordered piece: a leo heading at 30px inside a card reads as a section
   heading stuck to the card's ceiling. It is brandon tracked caps now, styled
   with the rest of the chart's apparatus. */
/* NOTHING DECLARED, DELIBERATELY. The column takes the grid's `align-items:
   start`, so its heading's box top is the card's top border: "let's have this
   section line up with the graphic" (09-17).

   It has been in three positions. Level with the card's top edge, which left a
   110px hole above it while the lead was outside the grid. Level with the lead's
   first line, once the lead moved in, which filled the hole and put the surplus
   underneath instead. Centred in a two-row span, which split the difference.
   The lead is outside the grid again and there is no hole to close, so the edge
   join is available and it is the one that was asked for — do not re-add
   align-self here without checking what is above the card. */

/* 1.25rem UNDER THE TITLE, NOT 2rem. 10-01: "this spacing feels off", on the
   gap between this heading and the paragraph below it.

   It was the only title-to-copy gap on the page that was not 1.25rem. Measured
   at 1280, every heading on the page against the block directly under it:

     every h2 -> its .hv4-lead            20px   (.hv4 h2 margin 0 0 1.25rem)
     .hv4-aside-title -> its lead         20px
     .hv4-gov h3 -> its first <p>         32px   <- the odd one out
     condition card h3 -> its copy        24px   (inside a card, tighter frame)
     Extract/Enhance/Exploit h3           10px   (a label on a sentence)

   A TITLED BLOCK IS THE SECTION'S OWN h2-AND-LEAD PAIR ONE SIZE DOWN: same
   spacing, same inherited colours, only the title's size differs. That is the
   rule this block is built on, and 2rem was quietly breaking the half of it
   that is about spacing. The two card values above are NOT counter-examples —
   both sit inside their own bordered frame, where the padding sets the rhythm.
   This heading sits on the open page beside the chart, so it answers to the
   page.

   The extra 12px read as a gap rather than as generosity because the heading
   is only 1.2 line-height and wraps to two tight lines at most widths, so the
   space under it was larger than the space inside it. */
.hv4-gov h3 {
    font-size: clamp(1.5rem, 2vw, 1.875rem);
    line-height: 1.2;
    margin: 0 0 1.25rem
}

/* BACK TO PROSE on 09-16. What was here — .hv4-gov-beats, .hv4-gov-beat,
   .hv4-gov-beat + .hv4-gov-beat and .hv4-gov-label — is deleted with the markup
   it styled. The three arguments worth keeping out of those notes:

   1. The hairline under a label belongs to the LABEL, not to the top of the
      block. A rule on a block's top edge divides a column into compartments and
      needs a fourth line to close the set, which turns an argument into a table.
   2. Coloured 2px rules were tried and taken back out: three of them stacked in
      a narrow column turned one argument in three beats into three titled blocks
      competing with the chart beside them.
   3. --bright-blue was settled for the labels after three attempts, and green was
      reverted on sight twice ("go back to the bright blue text") because green is
      the chart's own series colour, so a green label beside the chart reads as a
      key to it. If a label ever comes back in this column, it comes back blue.

   Paragraphs now, at the size and colour the beats' own copy already had — this
   is the same voice, it has just stopped being chopped up. 1.25rem between them
   rather than the beats' 2rem: the gap was carrying the separation the labels
   used to make, and prose paragraphs in one argument want less air than titled
   blocks do. */
.hv4-gov p {
    margin: 0;
    font-family: "leo", serif;
    font-size: 1.0625rem;
    line-height: 1.6;
    color: var(--spend-body)
}

.hv4-gov p+p {
    margin-top: 1.25rem
}

/* *** 991, THE SAME RUNG EVERY OTHER TWO-COLUMN LAYOUT ON THIS PAGE TURNS AT. ***
   09-30, about this section at tablet: "it's actually weird that it wasn't two
   columns. all things should break at the same spots so all two columns go to
   one column at same time. consistency is key."

   THE WHOLE MAP, so the next change does not have to rediscover it. ⚠️ THIS IS
   THE CURRENT STATE AS OF 10-13 — re-measured in full, not patched:

     TWO-COLUMN LAYOUTS (prose beside ONE other thing) -> 991
       .hv4-delivery-head  copy + figure               -> 1 at 991
       .hv4-why-grid       copy + copy                 -> 1 at 991
       .hv4-cost-grid      chart + governance copy     -> 1 at 991
       .sai-split          copy + drawing              -> 1 at 1199  <- see below
       .cta-learning-grid  copy + photo                -> 1 at 767   <- see below

     CARD GRIDS (n equal things) -> 767
       .hv4-conditions     4 cards -> 2 at 1199        -> 1 at 767
       .hv4-steps          6 cards, 2 across           -> 1 at 767

     THREE ACROSS -> 991, STRAIGHT TO ONE
       .hv4-rg             3 circles                   -> 1 at 991
       .hv4-order          3 cards                     -> 1 at 991

   TWO-COLUMN LAYOUTS AND CARD GRIDS ARE TWO DIFFERENT RULES, AND EACH IS
   CONSISTENT WITH ITSELF *AND* WITH styles.css. A two-column layout is prose
   beside one other thing and it turns at 991 — eight blocks in the standard do
   this (.deliver-row, .fa-case-situation, .fa-case-approach, .ve-framing-grid,
   .ve-step-grid, .ve-sidebyside-grid, .cn-flow-row, .cn-cta-grid). A card grid
   is n equal things and it turns at 767, which is .transform-cards' and
   .pa-cap-grid's rung site-wide.

   ⚠️ THE TWO APPARENT EXCEPTIONS ARE BOTH DELIBERATE AND BOTH HAVE A PRECEDENT
   IN THE STANDARD, so do not "fix" either into 991:

     .sai-split at 1199 — its drawing is a fixed viewBox at width:100%, so below
     1199 in two columns the labels fall to 8px. Measured 10-08. The standard's
     #ve-tool .ve-results-grid is also a figure-beside-copy panel and is also
     1199.

     .cta-learning-grid at 767 — that is the STANDARD's own value, unchanged,
     and the component is shared by 21 templates. Moving it to 991 would put the
     homepage out of step with twenty other pages to put it in step with four
     blocks on this one. Site consistency wins over page consistency here.

   ⚠️ .hv4-order WAS AT 1024 UNTIL 10-13 AND IS NOT ANY MORE. The full survey and
   the arithmetic are on the @media block itself; the short version is that 1024
   was borrowed from .pa-cap-grid, which at 1024 goes to TWO columns rather than
   one, so the number never meant here what it means there.

   1199 WAS ITSELF A CORRECTION OF 1439, AND THE SAME FACT UNDOES BOTH. The 1439
   arithmetic was sound and every input to it is now false: five slots needed a
   52px bar to hold "$18,000", so an 82px slot, a 410px plot, 156px of gutters,
   a 566px track, a 1146px container — which the 1.15fr track does not reach
   until 1440. The in-bar dollar figures were deleted on 09-16, so NOTHING SETS
   A MINIMUM BAR WIDTH ANY MORE, and the left gutter went to 0 with the axis
   ticks. There is no width this chart has to reach. Measured two-up at 3rem:

              container   chart   plot   bar   gov column
     1199       1043       532     366    73      463
     1024        891       451     285    57      392
      992        863       436     270    54      379

   Nothing breaks at the new floor: 54px bars at 992 are WIDER than the 51px the
   1200 two-up ran at 5.5rem, the two annotations keep their 8px of vertical
   clearance all the way down, and the prose column holds 379px — about 45
   characters of 17px serif, which is the floor for a measure and not under it.
   Stacked at 992 the plot is 697px and the bars are 139px: the "sparser, not
   bigger" failure the 1439 note already described, one device further down.

   THE GAP IS WHAT PAYS FOR IT. 5.5rem is a >=1200 luxury like every other
   gutter on this page, and 3rem is the number .hv4-delivery-head and .hv4-steps
   both drop to in this exact band, so the page's tablet gutter is one value.
   Written as `0 3rem` and not the shorthand — there is only one row here, and a
   row gap declared on a one-row grid is a number that means nothing until
   someone adds a row and cannot tell where it came from.

   THE PHOTOGRAPH FOLLOWS THIS. #hv4-cost::before is masked to sit on whichever
   side the prose is, so holding two columns down to 991 keeps the mountain
   behind the governance copy for the whole band instead of behind a full-width
   stack. Nothing in that rule needs changing; it just gets to stay right. */
@media(max-width: 1199px) {
    .hv4-cost-grid {
        gap: 0 3rem
    }
}

/* One column, so chart and copy stack in source order — which is the same order
   the two-column row reads in, now that the flip is off. 3.5rem is the row gap
   this rule carried at 1199 before it moved down.

   No grid-area to reset on the children. The warning above .hv4-cost-grid
   describes a three-child layout that no longer exists: the grid has two
   children, .hv4-spend and .hv4-gov, and neither is placed. */
@media(max-width: 991px) {
    .hv4-cost-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 3.5rem
    }
}

@media(max-width: 767px) {
    /* *** THE LEFT GUTTER IS 0 BECAUSE THERE IS NOTHING LEFT TO PUT IN IT. ***
       This was 2.75rem, and it was correct when `.hv4-spend-rule--bare` and
       `.hv4-spend-rule span` drew the $220K / $200K / $180K / $170K ticks out
       there. Those rules were deleted with the ticks (see the note above
       .hv4-spend-plot) and this reservation was left behind, so at 375 the chart
       was holding 44px of empty ground on the left — a third of the plot. The
       base value has always been 0px; this override was the only thing keeping
       the gutter alive anywhere on the page.

       This is the same failure the ⚠️ in css_traps.md describes: deleting an
       element leaves behind the rules that MEASURED it, and those rules keep
       applying against nothing. Nothing about the tick labels appears in this
       declaration, which is why grepping their class names did not find it.

       Measured at 375 before: frame 280px wide, of which the plot got 144.
       After: the plot gets the full 280. */
    .hv4-spend {
        --spend-gutter: 0px;
        --spend-key-gutter: 7.25rem;
        --spend-plot-h: 220px
    }

    /* *** THE LEGEND NO LONGER STEPS DOWN — IT KEEPS THE CARD'S 12px FLOOR AND
       THE STRIP WIDENS TO HOLD IT INSTEAD. *** This was 10px in a 5rem strip,
       sized so "DEVELOPER" — the longest unbreakable string in either label —
       fitted the 67px left over once the swatch and its gap took 12.8.

       Jess, 09-30, asked for the chart labels to come up, so the constraint is
       inverted: the type is fixed at .75rem and the STRIP is solved for. At
       12px with .14em of tracking "DEVELOPER" is ~93px, so the text column
       needs 6rem and the strip 7.25rem once the swatch, the gap and the .75rem
       offset from the plot are paid for.

       That width is only spent between 481 and 767. At 480 and below the legend
       leaves the gutter entirely (--spend-key-gutter: 0) and sits under the
       chart on one line each, so the extra 20px costs the plot nothing on a
       phone — it is charged to tablet, where the plot has it to give.

       The swatch keeps its base size rather than shrinking: a chip that does
       not shrink when its label does stops reading as a sample and starts
       reading as a bullet, and now neither one shrinks. */
    .hv4-spend-key {
        --spend-key-w: 6rem;
        letter-spacing: .1em
    }

    /* The title steps down one notch on a phone — 13px to 12px, the card's
       floor, not below it. It was 11px, set when the whole apparatus stepped
       down together.

       The size is not what makes this fit. "COST PER DEVELOPER, PER YEAR" is
       247px at 12px/.18em against a 280px content box at 375, which clears; the
       string only gets tight if the TRACKING stays at .18em, so that is what
       eases to .12em here. Keeping the band off the full width of the card is
       also what makes it read as a label rather than a heading.

       The band's bleed is still the card's padding, unchanged at 1.5rem here, so
       the margin stays -1.5rem. Its vertical padding comes down with the type. */
    .hv4-spend-title {
        margin-bottom: 1.25rem;
        padding: .75rem 1.5rem;
        font-size: .75rem;
        letter-spacing: .12em
    }

    /* No step down for .hv4-spend-ai any more. Five slots in a 383px plot puts
       the bars at 49px, which used to clip "$18,000" at leo 15px — 50px of
       text. At brandon 11px with .04em of tracking the same string is 41px, so
       it clears the narrowest bar the chart ever draws with room over. */

    /* *** "AI ARRIVES" ENDS AT ITS LINE BELOW 768 INSTEAD OF STRADDLING IT. ***
       The handoff's `text-anchor: end` rule for compact widths, and the one
       place the 09-17 instruction ("have AI Arrives centered above it") is not
       honoured. Centred, it runs into "Investment Hurdle Rate".

       ⚠️⚠️ IT IS 481–767 ONLY SINCE 10-16 — THE ≤480 BLOCK PUTS THE LABEL BACK
       OVER ITS LINE, which is what was asked for ("ai arrices shoudl be above
       the line", "on mobile"). This band keeps the exception because it is the
       narrowest PLOT on the page, not the narrowest page: the legend still sits
       in the right gutter down to 481 and takes 92px of plot with it, and both
       strings are fixed widths competing for what is left. Measured at 481,
       centred: the two labels overlap by 21px with 4px of vertical clearance.
       Below 481 the legend leaves, the plot jumps 257 → 372, and the same
       centring has 72px of room.

         vw    plot   centred "AI Arrives"   gap to hurdle label
         767   533    183–244                comfortable
         576   457    106–167                28px
         481   257     77–129                −21px  ← the exception's whole job
         480   372    123–175                 72px
         375   280     86–138                 17px
         320   230     66–118                −13px, two stacked lines

       ⚠️ 481 BEING THE WORST WIDTH IS THE POINT AND IT IS NOT OBVIOUS — a rule
       written at 480 would have missed it entirely, which is why this one lives
       at 767. ⚠️ And the overlap is survivable at 320 but not at 481 because of
       the VERTICAL gap, not the horizontal one: 5px apart the two read as two
       stacked annotations, 4px apart at a size one step larger they read as
       collision. If the labels' rows are ever brought closer, re-measure both.

       .25rem is the gap to the line; translateX(-100%) replaces the base rule's
       -50%. */
    .hv4-spend-arrive-label {
        transform: translateX(-100%);
        margin-left: -.25rem
    }
}

/* *** ALL FIVE YEARS RENDER ON A PHONE. THE CHART IS NOT ALLOWED TO DROP DATA
   TO FIT. *** (09-25, asked directly: "show all five years".)

   What was here until 09-25: `.hv4-spend-col--drop` and `.hv4-spend-year--drop`
   hid columns 01, 04 and 05 below 576, leaving years two and three — the pair
   the step lives between. The reasoning was sound on its own terms and it is
   recorded here rather than deleted, because it will be re-proposed by anyone
   who measures a 34px column: five slots in what was then a 144px plot gave
   28px bars, and the bars used to carry "$18,000" inside them at 41px wide.

   Two things had changed underneath it and neither was noticed:

     1. The in-bar value labels are GONE. Nothing inside a bar constrains its
        width any more, so a narrow bar is merely narrow, not broken. Every
        surviving note about 52px / 53px / 58px minimums is measuring a string
        that no longer exists.
     2. The plot was only 144px because of the two gutters, and BOTH were dead
        weight — the left one reserved for deleted tick labels (see above), the
        right one a 92px strip holding a legend that has somewhere better to be
        on a phone.

   Recover those and the plot goes 144px -> 280px at 375, which draws five 45px
   bars. The chart that was being protected from five columns had never actually
   been given the width to try.

   A chart that silently shows two of five years is not a smaller version of the
   argument, it is a different and weaker argument: the claim is that spend
   compounds across the WHOLE five years, and years four and five are where the
   compounding is visible. Do not reintroduce a drop scheme. If something has to
   give at a narrower width, it is the plot height, the label size, or the
   legend's position — never a year. */
/* *** 480, NOT 575. *** 575 is Bootstrap's `sm` and it appears nowhere in
   styles.css; the site's small-phone rung is 480, always paired with a
   `min-width: 481px` band above it (see #pa-hero). Nothing about this chart
   needed 575 specifically — it was the width the two-column fallback happened to
   be tuned at, and that fallback is gone.

   Checked across the band this hands back to the gutter layout, 481–575: at 575
   the plot is 366px and at 481 it is 272px, both drawing five bars with the
   legend still in the right strip, and the two annotations clear at both ends. */
@media(max-width: 480px) {
    /* 200px against a 280px plot at 375 is 1.4:1, and 1.9:1 at 480 — a
       rectangle across the whole range, which is what this was always for. It
       was 160px, set when dropping to two columns left a 206px plot and 220px
       made the box taller than it was wide. Five columns in the full width is a
       wider box than either, so the height can come back up, and it has to: the
       two serif annotations sit 10 points of scale apart vertically, which is
       16px on a 160px plot and 20px on a 200px one against a 19.6px line box.

       Room for the legend below comes from the card's padding, not from here. */
    .hv4-spend {
        --spend-plot-h: 200px;
        padding-bottom: 3rem
    }

    /* *** THE LEGEND DROPS BELOW THE CHART AND THE RIGHT GUTTER GOES WITH IT.
       ***

       This is the one structural idea taken from the supplied mobile-chart
       handoff, which puts the legend beside the plot on wide screens and under
       it below ~520px. The rest of that handoff is a JS component that measures
       its container with a ResizeObserver and redraws an SVG; this chart is
       HTML and CSS with one SVG path in it, and it does not need any of that to
       be fluid. It needs to stop reserving width it cannot afford.

       92px of a 280px card is a third of the chart spent on two words. Below
       the plot the same two labels sit on one line each, side by side, in space
       that costs nothing but height.

       The keys stay children of .hv4-spend-plot — moving them in the markup
       would cost the desktop layout its anchor, since their vertical positions
       are percentages of the plot. `top: calc(100% + 4rem)` is measured from
       the plot's own bottom edge: 25px of year numerals, 23px of "YEARS", and
       16px of air. Those two heights are fixed px type and do not move with the
       viewport, but they ARE the two numbers that make 4rem correct — if either
       row changes size, re-measure. The card's padding-bottom is what keeps the
       result inside `overflow: hidden`. */
    .hv4-spend {
        --spend-key-gutter: 0px
    }

    .hv4-spend-key {
        top: calc(100% + 4rem);
        bottom: auto;
        width: auto;
        white-space: nowrap;
        transform: none
    }

    .hv4-spend-key--ai {
        left: 0
    }

    /* *** THE SECOND KEY HANGS OFF THE RIGHT EDGE, NOT OFF THE HALFWAY MARK. ***
       This was `left: 50%`, which was fine while the label was 10px and stopped
       being fine the moment it went to the card's 12px floor: `left: 50%` puts
       a FIXED-WIDTH nowrap string at a PROPORTIONAL start, so the wider the
       label and the narrower the plot, the further past the right edge it runs.
       At 320 that was 19px of "DEVELOPER COST" disappearing under the card's
       `overflow: hidden` — and because it was clipped rather than spilling, the
       page reported zero horizontal overflow the whole time. It was found by
       comparing the key's own right edge against the card's content edge.

       Anchored right, both ends are fixed instead and the GAP between the two
       keys is what absorbs the narrowing:

         vw    plot   "AI SPEND" ends   "DEVELOPER COST" starts   gap
         480   372    88                238                       150
         375   280    88                146                        58
         320   230    88                 96                         8

       8px at 320 is thin but they are two separate lockups with their own
       swatches, and 320 is below the site's smallest rung anyway. If a third
       key is ever added, this stops working and the row has to stack. */
    .hv4-spend-key--dev {
        left: auto;
        right: 0
    }

    /* *** 12px, DOWN FROM 13, 10-16: "let's make this text just a little bit
       smaller because it's too close to the ai arrives line that it's
       confusting" — pointing at "Investment Hurdle Rate" on a phone. *** It is
       the other half of the edit that centred "AI Arrives" over its line in the
       ≤767 block above: centring gives the grey label back the 3px of its own
       line it was dodging, and the orange one gets out of its way by being
       shorter. 135px → ~125px at 390, which is the gap that edit spends.

       ⚠️ BOTH LABELS STEP TOGETHER AND ALWAYS HAVE. They are one annotation
       voice — 14px leo italic 500, one grey and one orange — and sizing only
       the one that was complained about would leave two italics a pixel apart
       claiming to be the same instrument. The selector is the record of that;
       do not split it to tune one.

       Below is the derivation this started from, at 13px — the handoff's own
       `compact ? 13 : 15` annotation rule, and the only type on this chart that
       steps down below 480.

       It buys horizontal room at the ONE width where the two annotations still
       come within a few pixels of touching, and that width is not 375 — it is
       320. They are fixed-width strings sitting on rows only 2px apart
       vertically, so the horizontal gap is the only thing keeping them apart,
       and it tracks the plot:

         vw    plot   gap between the two labels, at 14px
         480   372    82px
         375   280    26px
         320   230    −3px

       At 13px "Investment Hurdle Rate" loses 10px and "AI Arrives" 4px, which
       turns 320 into a 7px gap. Nothing at 375 or above needed it. Applied
       across the whole band rather than at a 320 breakpoint, because inventing
       a rung below the site's smallest is the thing this pass exists to undo. */
    .hv4-spend-arrive-label,
    .hv4-spend-hurdle-label {
        font-size: .75rem
    }

    /* *** BACK OVER ITS OWN LINE, 10-16: "ai arrices shoudl be above the
       line" / "on mobile". *** This undoes the ≤767 end-anchor for phones only
       and restores the base rule's centring, which is the 09-17 treatment
       ("have AI Arrives centered above it") — one behaviour at the two widths a
       phone actually is, and at 375 the label sits 86–138 against a hurdle
       label starting at 155.

       ⚠️ IT IS SPELLED AS translateX(-50%) AND margin-left:0, NOT AS `unset`.
       The transform is also carrying the base rule's centring, so dropping the
       override would leave the label starting AT 40% rather than centred on it;
       the two declarations above have to be answered one for one.

       ⚠️ WHY IT CANNOT GO IN THE ≤767 BLOCK INSTEAD: at 481 the legend is still
       in the right gutter and the plot is 257px, where centring overlaps the
       hurdle label by 21px. The whole working is in that block's note. */
    .hv4-spend-arrive-label {
        transform: translateX(-50%);
        margin-left: 0
    }
}

/* ==========================================================================
   6. Conditions — light band waved in from the blue cost section and out into
      the teal correction section, exactly like #deliver on the RB home.
      Cards copy the "Our Approach" language from the RB homepage: .tf-card on
      .transform-section — four solid brand-colour cards, white type, and a
      ghosted brandon numeral bled off the top right corner.
   ========================================================================== */

/* Layout only — the band takes its background from a .hv4-section--* modifier. */
.hv4-waveband {
    padding: 0;
    position: relative
}

.hv4-waveband-inner {
    padding: 3rem 0 5rem
}

/* *** THE TOP WENT 2rem -> 3rem, 10-08: "the top paddin glooks a little tight
   too", pointing at the wave running into "Four Conditions You Can Test This
   Week". ***

   ⚠️⚠️ A CURVE DOES NOT HAVE ONE CLEARANCE, IT HAS A RANGE, AND EVERY NOTE ON
   THIS PAGE HAS QUOTED WHICHEVER END SUITED IT. Sampled on the top waves'
   path, the colour boundary runs from 20.3 to 73.2 of the 90-unit viewBox —
   at the 70px desktop box that is 15.8px to 56.9px down. So the section's own
   ground above the padding is 54.2px at the loosest point and 13.1px at the
   tightest, a 41px spread across one edge:

       #hv4-conditions, 1440    x=400   86px of ground    the curve's crest
                                x=1180  45px              the curve's trough
       and the h2 runs 143->978, so its own left end sits at 71px

   The 2rem was set against the 54.2 reading and the 45 is what Jess saw. Both
   numbers were in the file; the tight one was never the one that got quoted.
   ⚠️ TAKE THE TROUGH. A reader measures a gap at its narrowest, the same way
   feedback_wave_padding.md already says to measure a wave from its ink.

   3rem is +16px at every width and it is deliberately NOT the ~83px that would
   put the trough on the page's plain 96px — see the note below, where the same
   +16px was the answer to the same complaint at this band's other edge on
   09-30 ("the curve is what is being paid for, and it is worth about 16px, not
   52"). That verdict was about the bottom; it transfers.

   ⚠️ THIS IS THE SHARED RULE AND BOTH ITS USERS HAVE THE SAME DEFECT.
   #hv4-conditions and #hv4-why are the only sections with a wave-top, they
   carry the same curve mirrored, and the trough therefore lands on opposite
   sides — x=1180 here, x=260 there, which at 143 is UNDER #hv4-why's own h2
   and tighter still at 49px. Fixing only the section that was reported would
   have left the worse one. #hv4-scaled's top is an ID rule in hv4-scaled-ai.css
   and is not affected; it has no wave at all.

   *** THE ONE JOIN THAT WAS NOT ON THE PAGE'S RHYTHM, 09-22: "follow the typical
   spacing we do from one section to the next so the page feels super
   consistent." ***

   A WAVE IS PART OF THE PADDING, MEASURED FROM ITS HIGHEST POINT — that is the
   standing rule (feedback_wave_padding.md), and it is why .hv4-waveband-inner's
   2rem top is right: 70px of wave + 32px = 102px, which is 6rem give or take.
   The inherited 5rem BOTTOM had no wave subtracted from it, so the wave-bottom's
   70px was added on top of a full 80px and this join ran 246px against the
   page's 192. It was the longest gap on the page by 54px.

   2rem makes the band symmetrical — 102px of clearance above and below its own
   cards — and brings the join to 198px, which is every other wave join on the
   page and 6px off the plain ones. Saves 48px of height, which is the direction
   the brief runs.

   ⚠️ THIS NOTE USED TO SAY #hv4-why KEEPS THE 5rem BECAUSE ITS BOTTOM WAVE IS A
   HOLE OVER THE CTA BAND. #hv4-why HAS NO BOTTOM WAVE. Verified 09-30: its only
   SVG child is .wave-top, and #cta-learning has none at all. See that section's
   own rule below for what actually governs its bottom.

   *** 2rem -> 3rem, 09-30: "feels kind of tight on desktop." ***

   ⚠️ "70px of wave" above is the SVG BOX, not the ink, and the two are not the
   same number. The bottom wave's curve reaches its highest point 15/90 up the
   box — 11.7px at the rendered 70px — so the navy stops 11.7px below the box
   top at x≈900, not 70px. Real clearance from the aside's last line to the
   colour edge was 32 + 11.7 = 44px at that x, against 96px on every plain
   section. Measure a wave from where the INK actually lands, per
   feedback_wave_padding.md, and sample the bezier — control points lie off the
   curve, so reading the `d` attribute's numbers overstates it.

   3rem takes the tightest point to 60px and the join to 213px, inside the
   page's existing 176–221 spread. It is deliberately not the ~84px that would
   make the colour edge match a plain section: that lands the join at 250px,
   which is the 246px 09-22 rejected. The curve is what is being paid for, and
   it is worth about 16px, not 52. */
#hv4-conditions .hv4-waveband-inner {
    padding-bottom: 3rem
}

/* *** #hv4-why's BOTTOM WAS THE TIGHTEST JOIN ON THE PAGE, NOT THE LOOSEST. ***
   09-30, and it was reported to Jess the wrong way round first.

   ⚠️ HOW THE ERROR HAPPENED, BECAUSE IT WILL HAPPEN AGAIN: #hv4-why is two
   columns and they do not end level. The right column stops at the Get in Touch
   button, 198px above the left column's last paragraph, and the button is the
   last element IN THE DOM. Measuring the join off `lastElementChild` gave 359px
   against the page's ~195 and read as a hole. Measured off the column that
   actually reaches the bottom, it is 160px. **For a join, take the lowest
   rect.bottom of any leaf in the section — never DOM order.**

   The real figure: 5rem here + #cta-learning's own 80px top = 160px, against
   192px for a plain join and 198-221 for the waved ones. #cta-learning is a
   shared component running on other pages, so its 80px is not ours to move;
   7rem on this side alone brings the join to 192px exactly, which is the plain
   number the whole page is measured against.

   There is no wave to subtract here, which is the one thing that makes this
   join simple — see the correction in the note above. */
#hv4-why .hv4-waveband-inner {
    padding-bottom: 7rem
}

/* .transform-cards — the grid stands in for the Bootstrap row, gap 1.5rem
   matching .g-4. Three named rows (title / body / test) that the cards adopt
   as a subgrid, so the test panel lands at the same height on all four
   regardless of how many lines each card's copy runs to.

   *** THE `1fr` IS ON THE TEST ROW, NOT THE BODY ROW, AND THAT IS THE WHOLE
   POSITION OF THE PANEL. *** Jess, 09-30: "let's move the frosted box up a bit
   more it feels low."

   The row that carries `1fr` is the one that eats the slack when a card is
   shorter than the tallest card. It used to be the BODY row, which pushed the
   test to the bottom of every card and left 44px of dead ground above it —
   measured at 1440, against the 24px grid gap that was supposed to be there.
   Moving the `1fr` to row three makes the panel grow UPWARD into that slack
   instead: the gap above it is the grid gap on every card, and the extra
   height lands inside the box as the empty ground Jess asked for.

   So the three rows now read auto / auto / 1fr, and the order matters — an
   `auto` body row is exactly as tall as its copy and nothing more. */
.hv4-conditions {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-template-rows: auto auto 1fr;
    gap: 1.5rem;
    margin-top: 2rem
}

/* .tf-card — the four colours run at full strength, as the facade page's
   .tf-card ramp does.

   A navy veil over all four was tried here and taken back out. It bought
   contrast, but it turned four bright cards into four dark ones sitting on a
   dark section, and the row stopped being the brightest thing on the page,
   which is the job it has. The copy is carrying the readability instead: 17px
   and fully opaque, up from 15.2px at 78% white.

   That does not get the two lightest cards to 4.5:1 — see the note on the
   paragraph rule. It is a deliberate call in favour of the palette.

   *** THE BOTTOM PADDING EQUALS THE SIDES, 09-30. *** Jess: "I want equal
   padding on sides and bottom." It was 2rem against 1.75rem at the sides — a
   4px difference nobody could see while the block at the bottom of the card
   had no edges. The frosted panel gave it a hard edge to be measured against
   and the inset stopped matching all the way round, so the bottom comes down
   to 1.75rem and the three visible sides are one number.

   *** AND NOW THE TOP JOINS THEM, 10-08: "the top bottom and left right padding
   should be the same." *** One number on all four sides, 1.75rem.

   ⚠️ THE OLD NOTE SAID THE TOP "CLEARS THE GHOST NUMERAL, NOT THE COPY", AND
   THAT IS WHY IT WAS 2.25rem. It does not survive the ask, and it was weaker
   than it sounded: .hv4-condition-num is position:absolute and pinned to the
   card's own top edge, so it does not move with the padding at all — the 8px
   only ever changed where the h3 sat ON the numeral, not whether the numeral
   was cleared. The numeral is 152px tall against a 36px inset either way; the
   h3 has always overlapped it and still does, 8px higher.

   ⚠️ If the top is ever raised again, raise ALL FOUR. The 09-30 ask and this
   one are the same ask twice, and the only reason it came back is that the top
   was left out of the set the first time.

   *** row-gap: 1rem — "the spacing here feels a bit much", 10-16, pointing at
   the gap between the hairline under the title and the body copy. *** It was
   24, and 24 was never chosen for this seam: it is .hv4-conditions' own
   `gap: 1.5rem`, inherited because `grid-template-rows: subgrid` takes the
   parent's gutters along with its tracks.

   ⚠️⚠️ A SUBGRID ITEM CAN SET ITS OWN row-gap, AND THAT IS THE ONLY LEVER THAT
   DOES NOT MOVE SOMETHING ELSE. .hv4-conditions' gap is doing two jobs at once
   — the gutters BETWEEN the four cards and the seams INSIDE each of them — so
   lowering it there would have pulled the cards together as well, and below 767
   (one column) that is the vertical gap between the cards themselves.
   Overriding row-gap here reaches only the seams inside the card; verified, the
   card-to-card gap stays 24 at 390.

   ⚠️ IT TAKES THE BODY→TEST SEAM WITH IT, WHICH IS WHY THE RULE ON
   `.hv4-condition p:not(.hv4-test)` IS BACK. That seam was settled at 24 on
   10-08 ("there's too much space before the test part") and the frosted panel
   is a separate block from the body; 16 there would set it at the same distance
   as the title's own seam. 16 + 8 = 24, unchanged. The two seams want two
   numbers and one grid can only give one — see that rule's note for why the
   `:not()` is load-bearing. */
.hv4-condition {
    position: relative;
    overflow: hidden;
    background-color: #7fae96;
    border: 0;
    border-radius: 8px;
    box-shadow: 0 2px 16px rgba(6, 41, 53, .09);
    padding: 1.75rem;
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 3;
    row-gap: 1rem;
    align-content: start;
    color: var(--white);
    transition: box-shadow .25s
}

.hv4-condition:hover {
    box-shadow: 0 8px 32px rgba(6, 41, 53, .15)
}

.hv4-condition:nth-child(1) {
    background: var(--light-green)
}

.hv4-condition:nth-child(2) {
    background: var(--dark-green)
}

.hv4-condition:nth-child(3) {
    background: var(--bright-blue)
}

.hv4-condition:nth-child(4) {
    background: var(--blue)
}

/* .tf-card-num — the first card sits on the lightest ground, so it carries a
   stronger ghost than the other three.

   *** THE NUMERAL SITS WHOLE INSIDE THE CARD AND IS NOT CROPPED. *** Jess,
   09-30, on the phone layout: the "01" was being sliced through the middle of
   its strokes at the card's edge rather than reading as a deliberate bleed.

   This was `top: -0.1em; right: -0.05em`, inherited from `.tf-card-num` on the
   facade pages, where the numeral genuinely is meant to bleed off the corner.
   The two situations are not the same. There the crop is small against a wide
   card; here the card carries `overflow: hidden`, so at 375 a 330px card was
   cutting 7px off the right of the glyph and 14px off the top — enough to take
   the curve off a "0" and leave a flat edge, which reads as a mistake rather
   than a crop.

   Zero on both axes puts the numeral's box at the card's own edges. The glyph
   still does not touch them, and that is the point: at line-height 1 on a 144px
   type the half-leading holds it about 14px down from the top, and the .02em of
   trailing letter-spacing holds it about 3px in from the right. So it reads as
   a large number placed in the corner rather than one sliding out of it.

   ⚠️ This is a LOCAL divergence from `.tf-card-num`, which the facade pages
   still run cropped. Do not "restore consistency" by copying the negative
   offsets back — they were asked to come out. */
.hv4-condition-num {
    position: absolute;
    top: 0;
    right: 0;
    font-family: "brandon-grotesque", sans-serif;
    font-weight: 700;
    font-size: 9.5rem;
    line-height: 1;
    letter-spacing: .02em;
    color: hsla(0, 0%, 100%, .18);
    pointer-events: none
}

.hv4-condition:not(:nth-child(1)) .hv4-condition-num {
    color: hsla(0, 0%, 100%, .1)
}

/* .tf-card h4 */
/* .tf-card h4

   1.25rem of padding-right, not 2.25. The padding is there to keep the title
   off the ghost numeral, but it was never doing that job: the numeral is 152px
   wide and the padding reserved 36px, so the titles have always run over its
   left edge — deliberately, because at 10-18% white it is a texture rather
   than an object.

   What the extra 16px did do was cut the title measure to 179px in a 215px
   column, which is narrower than two of these words set together. That is what
   put "Organized Around Ownership" on three lines while the other three sat on
   two, and shortening the copy could not fix it: at 179px no two-word pair in
   the set fits on one line. At 195px they all do, and every title in the set
   is two lines. */
.hv4-condition h3 {
    position: relative;
    font-family: "leo", serif;
    font-size: 1.45rem;
    font-weight: 500;
    color: var(--white);
    line-height: 1.2;
    margin: 0;
    padding-right: 1.25rem
}

.hv4-condition h3::after {
    content: "";
    display: block;
    width: 48px;
    height: 1px;
    background: hsla(0, 0%, 100%, .4);
    margin-top: 1rem
}

/* .tf-card p

   Full white at 17px, up from 78% white at 15.2px. Both halves of that matter:
   the tint is a device for dark grounds, where it holds body copy a step below
   a white heading and still clears contrast easily, and on these four it was
   spending headroom the copy did not have. The heading stays a step above by
   being 23px serif against 17px sans, which is the difference the reader
   actually sees.

   Contrast, measured on the four grounds at full strength: 2.34, 3.87, 3.06
   and 6.20 to one. WCAG AA wants 4.5 for text this size, so cards one to three
   are under it — card one badly. Nothing available in type fixes that; white
   is already the lightest value there is, and navy type would rescue cards one
   and three while breaking card four. Only the grounds can fix it, and the
   grounds are the point of the section. Flagged rather than silently traded:
   this is a live decision, not an oversight. */
.hv4-condition p {
    position: relative;
    font-size: 1.0625rem;
    font-weight: 400;
    color: var(--white);
    line-height: 1.6;
    margin: 0
}

/* *** THE SEPARATION FROM THE TEST IS DECLARED HERE, ON THE BODY, AND NOT ON
   THE PANEL. *** Jess, 09-30: "maybe a tad more padding between the body and
   the test. but keep the padding on sides and bottom how it is."

   The two halves of that are in tension and the selector is what resolves it.
   The panel is `height: 100%` in a grid row, so ANY margin on it offsets the
   box out of the row it was sized against and it overhangs into the card's
   bottom padding — that is the exact bug that produced 12px under the box
   against 28px at its sides an hour earlier. A margin here instead grows the
   body ROW, which is `auto`; the test row below it is untouched and the box
   keeps its inset on all three visible edges.

   `:not(.hv4-test)` is load-bearing, not tidiness. The test is itself a `<p>`
   inside `.hv4-condition`, so a bare `.hv4-condition p` would put the margin
   on the panel too and reintroduce the overhang it exists to avoid.

   16px on top of the grid's own 24px made 40px — the figure the 09-23 review
   arrived at ("need to make the test standout more and feel like it's own
   section"), when the same separation was carried by a divider RULE with 40px
   above it and 16px below. The panel replaced the rule; the 40 was carried
   over unchanged.

   *** THE 16px IS GONE, 10-08: "there's too much space before the test part."
   *** The separation is the grid's own 24px row gap and nothing else.

   ⚠️ THE 40 WAS A HAIRLINE'S NUMBER BEING SPENT ON A PANEL. A 1px rule has to
   borrow its weight from the space around it, so it needs a big gap above and
   a small one below to read as the top edge of a new block. A frosted panel
   draws its own edge on all four sides and carries that weight itself — which
   the note two rules down already said in as many words ("the asymmetric 40/16
   spacing that carried it is no longer load-bearing and comes out with the
   border"). It did not come out with the border; it outlived it by eight days.
   ⚠️ When a treatment is replaced, delete the spacing that was propping the
   old one up in the same edit.

   ⚠️ THE SELECTOR IS WHAT IS WORTH KEEPING HERE. If a bottom margin is ever
   wanted on the body copy again it goes on `.hv4-condition p:not(.hv4-test)`
   and NEVER on a bare `.hv4-condition p`. The test is itself a <p> inside the
   card and the panel is `height: 100%` in a grid row, so any margin on it
   offsets the box out of the row it was sized against and it overhangs into the
   card's bottom padding — 12px under the box against 28px at its sides, which
   is the bug that rule existed to avoid.

   *** AND IT IS A RULE AGAIN, 10-16, AT 8px RATHER THAN 16. *** Not a reversal
   of the 10-08 deletion: the seam it produces is the same 24 that deletion
   landed on. The card's row-gap went 24 → 16 to tighten the gap under the
   title's hairline ("the spacing here feels a bit much"), and because one grid
   gap serves both seams, this is what holds THIS one still. 16 + 8 = 24.
   ⚠️ IF THE row-gap EVER CHANGES AGAIN, THIS NUMBER IS ITS COMPLEMENT — the two
   are one edit, and the figure to preserve is the 24, not the 8. */
.hv4-condition p:not(.hv4-test) {
    margin-bottom: .5rem
}

/* No counterpart on .tf-card. The card's third subgrid row holds this, so the
   rule lands at the same height on all four whatever the copy above runs to.
   Inherits the body type from `.hv4-condition p` — the test is part of the
   argument, not a callout.

   *** SEPARATED 09-23, Miro review 7.2: "need to make the test standout more and
   feel like it's own section. X4" ***

   The measured cause was the SPACING, not the rule. It sat 24px below the body
   and 16px above its own text — near-symmetrical, so the rule read as a
   paragraph break inside one block rather than the top edge of a new one. A
   divider belongs to what it introduces, so the space above it now clearly
   exceeds the space below: 40px against 16px. The 24px came from the section's
   own `gap: 1.5rem`, so the extra 16px is added as margin here rather than by
   touching the grid, which would move every row on all four cards.

   *** THE PANEL IS NOW A PANEL. THE 09-23 PROHIBITION WAS LIFTED 09-30, BY THE
   PERSON WHO SET IT. *** Jess: "lean into the frosted numbers and do frosted
   boxes." That is a direct reversal of the review's "Do not add a background
   panel, a tint, or a border around the block", so it is recorded here rather
   than quietly dropped — the old constraint is not stale, it was overruled.

   The divider above was the answer available under a no-panel rule, and it
   worked. A panel does the same job more plainly, so the asymmetric 40/16
   spacing that carried it is no longer load-bearing and comes out with the
   border.

   *** THE TINT IS THE GHOST NUMERAL'S OWN VALUE, NOT A NEW ONE. *** .18 white
   is `.hv4-condition-num` verbatim — that is what "lean into the frosted
   numbers" means literally, and it keeps the section to one translucency
   instead of two that nearly match. Do not tune this without tuning the
   numeral, or the card grows a second frosted language.

   *** CONTRAST GOES UP BY ADDING WHITE, NEVER BY DARKENING. *** Unchanged and
   still the binding rule here: a dark scrim would be the intuitive way to make
   a panel read on these grounds and it is the one move this section does not
   allow. These are brand colours at full strength already carrying a flagged
   text-contrast shortfall (see `.hv4-condition p`), and a white tint LIFTS the
   ground under the copy — card one's 2.34:1 improves inside the panel rather
   than getting worse.

   No `backdrop-filter`. "Frosted" here is the look, not the mechanism; the
   ground behind the panel is a flat brand colour, so a blur would cost a
   compositing layer on four cards and render identically.

   *** ALL FOUR PANELS ARE THE SAME HEIGHT, AND THE SHORT ONE CARRIES THE SLACK
   AS EMPTY GROUND — that is `height: 100%` below. *** Jess, 09-30: "I want all
   the boxes to be the same size, so for box three that has less text, just let
   there be some white space in the box. I feel like the box being shorter is
   more glaring than it just being longer."

   The card is already `grid-template-rows: subgrid` over three rows, so row
   three is as tall as the tallest test across all four cards — the ROW was
   always equal. The PANEL was not, because it sized to its own text and left
   the remainder as card ground. That was invisible while the block had no
   background; the moment it became a box, four boxes of three different heights
   sat in a row of one. `height: 100%` makes the box the row rather than the
   text, and this is the matched-pair rule from `feedback_matched_pairs.md`:
   elements that read as a set are one size declared once, never content-sized.

   *** NO `margin-top` HERE, AND THAT IS NOT AN OMISSION. *** A margin on a
   `height: 100%` grid item offsets the box DOWNWARD out of the row it was
   sized against, so it overhangs into the card's bottom padding. The 1.25rem
   that used to be here ate 20px of a 32px bottom padding and left 12px of
   ground under the panel against 28px at its sides — the asymmetry Jess
   flagged as "I want equal padding on sides and bottom" in the same breath as
   "it feels low". The 24px of separation from the body copy is the section's
   own `gap: 1.5rem` and needs no help.

   *** THE PANEL'S OWN PADDING IS NOT THE CARD'S. *** 1.25rem inside the box is
   deliberately tighter than the card's 1.75rem so the panel reads as nested
   rather than as a second card. The two are independent; changing one does not
   imply the other.

   *** ONE NUMBER ON ALL FOUR SIDES, 10-16: "alos this spacing around should be
   consistent". *** The bottom was 1.375rem against 1.25rem everywhere else — an
   optical 2px for a box whose last line is a descender-heavy question. ⚠️ THIS
   IS THE SAME ASK FOR THE THIRD TIME ON THIS CARD (09-30 "I want equal padding
   on sides and bottom", 10-08 "the top bottom and left right padding should be
   the same") and the panel was left out of the set both times. An optical
   correction does not survive a request for equal padding, however well argued
   it is — equal is a thing the reader can check and 2px of optics is not.
   ⚠️ The panel is `height: 100%`, so its content never sits against the bottom
   padding anyway: the row is as tall as the LONGEST test and the shorter three
   carry the slack as empty ground. The 2px was answering a problem only card
   two ever had. */
.hv4-condition .hv4-test {
    position: relative;
    height: 100%;
    padding: 1.25rem;
    border-radius: 6px;
    background: hsla(0, 0%, 100%, .18)
}

/* *** STACKED, 09-30, BECAUSE THE PANEL GIVES IT SOMEWHERE TO STAND. *** This
   was deliberately a run-in lead-in — inline, so it carried no more weight than
   the sentence it opened — and that was the right call when the block had no
   edges: a stacked label floating on the card ground would have read as a
   second heading competing with the h3. Inside a box it reads as the box's
   label, which is what it is.

   The colon came out of the markup and the question gained a capital; the
   reasoning is on the template, next to the copy it changed.

   Still inheriting the paragraph's colour. It does not become white-on-tint or
   go up a size — the frost is already the emphasis, and doubling it would make
   the test louder than the h3 it sits under.

   Always uppercase, always tracked.

   *** .18em, UP FROM .14em, 09-23, Miro review 7.2. *** The approved treatment
   offers a choice — "Increase the weight, or the letterspacing, on the THE TEST
   label itself" — and tracking is the one to take: the weight is ALREADY 700,
   so the only way up is a heavier cut that this family may not load, and a
   synthesised bold on a 10.9px tracked cap would thicken and blur it rather
   than strengthen it. Tracking is also what the label already uses to read as a
   label, so more of it makes it more itself instead of introducing a new
   device. */
/* *** THE HAIRLINE RUNS RIGHT FROM THE LABEL — IT IS THE SITE'S EXISTING
   LABEL-PLUS-RULE LOCKUP, NOT A NEW DEVICE. *** Jess, 09-30: "what if we put a
   line next to 'the test' like on the card has a little line under the title."

   `.hv4-spend-title` on the cost chart is the same construction — flex, a gap,
   and a `flex: 1` hairline filling whatever is left — and that pattern is what
   lets a sub-12px tracked cap TITLE the block beneath it instead of just
   labelling the sentence it opens. Matching it keeps the page to one idea of
   what a small label looks like.

   *** IT RUNS TO THE PANEL EDGE, WHERE THE h3'S IS A FIXED 48px. *** Deliberate,
   and the difference is what keeps them from competing. The card title's stub
   sits under a 23px serif heading on open card ground and is an accent on it.
   This one has a box to span, and a box is exactly what a full-width rule
   needs — it stops at a real edge instead of a chosen length. A 48px stub here
   would read as a second, smaller version of the title's mark floating inside
   the panel.

   *** SAME INK AS THE TITLE'S RULE. *** `hsla(0,0%,100%,.4)` verbatim from
   `.hv4-condition h3::after`. One hairline value on the card, so the two read
   as the same instrument at two scales. It is white-on-colour here rather than
   the light-ground `rgba(6,41,53,.14)` — see `feedback_rules_and_hairlines.md`;
   the ground decides the ink.

   The `.5rem` that used to sit under the label as `margin-bottom` is now the
   flex `gap` to the LEFT of the rule as well, so the label is inset from its
   hairline by the same distance it stands off the question below it. */
.hv4-test-label {
    display: flex;
    align-items: center;
    gap: .625rem;
    margin-bottom: .5rem;
    font-family: "brandon-grotesque", "leo", sans-serif;
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase
}

/* The tracking puts a trailing space after the final letter, so the rule would
   start .18em further right than the label optically ends. Pulled back by that
   much rather than trimming the tracking, which is doing the labelling. */
.hv4-test-label::after {
    content: "";
    flex: 1;
    height: 1px;
    margin-left: -.18em;
    background: hsla(0, 0%, 100%, .4)
}

/* Aside — an important side point, set as plain body copy in the flow. This was
   a tinted, bordered box with a white tag bar and a speech-bubble notch on top.
   The notch pointed at nothing, and the boxing plus the smaller type made the
   copy read as a footnote when it is actually part of the argument. Only the
   box went; type, measure and colour come from .hv4-lead, so the aside is the
   same object as the section's own opening heading-and-lead pair, one size down.
   Used in #hv4-conditions and #hv4-delivery.

   The lead-in is 72px at desktop, up from 48. At 48 it was the same gap the
   section's own h2 leaves above its lead, so the aside title looked like the
   next paragraph of the block above it rather than the start of a new one
   ("can you give more padding above them so they are further down"). It has to
   clear the block above by more than that block's own internal spacing, which
   is 48px in both places — the .hv4-steps row gap in delivery and the
   .hv4-conditions grid gap in conditions. */
.hv4-aside {
    margin-top: clamp(2.5rem, 5vw, 4.5rem)
}

/* The copy runs the full width of the container, one line to a line — asked for
   twice ("what if the body copy for these two extended across the page not
   stopping halfway", then "not two columns one column across"). Dropping
   `.hv4-lead`'s `min(68ch, 100%)` is the whole change; the aside is the only
   place on the page where the lead measure is off.

   Two text columns were built first, on the reasoning below, and that is what
   was rejected. Keeping the reasoning because it is real and will come up
   again: 1155px at 17.6px serif is about 127 characters a line, and a reader
   loses the start of the next line returning past roughly 75. What buys that
   back here is that these are short blocks — three and four lines a paragraph
   at this width — so there are very few returns to lose your place on, and the
   title above sets the left edge for the eye to come back to.

   Do not re-offer `columns: 2`.

   THE TYPE is `.pa-bio-quote` from the leadership section (styles.css) — leo
   italic, 1.5rem, 1.5 line-height, full white — asked for by pointing at it:
   "I want to do the text like this for capabilities are the unit and start small
   sub sections". Copied rather than approximated, per the standing rule about
   borrowing a reference component.

   TWO GROUNDS as of 09-17. There are two asides and they no longer share a
   background: `#hv4-conditions` is still navy, `#hv4-delivery` went to the white
   textured paper. So the colour splits the way `.hv4-aside-title` right below
   already splits it — light by default, and a tinted white on the navy ground.

   TONED DOWN, 09-17 pm: "the body copy feels a bit too big and bold. let's lower
   the weight, lighten the color, and make it smaller but not as small as body
   copy. apply to both sections with this style." Three levers, all moved at once,
   and the instruction names both asides so this rule stays the only place it is
   set.

   SIZE 24px -> 20px at 1440. The floor is 18px, not the 17px it looks like it
   should be: body copy is 16.8 and the section lead is 17.6, and "not as small as
   body copy" has to hold at EVERY width, not just the widest. A 17px floor would
   have collided with the lead below ~1290 and with body copy soon after. 18 keeps
   a visible step over both at every size. The clamp reaches its 20px cap at
   1429px, so it is flat across the desktop range and only moves below that.

   WEIGHT 400 -> 300. `leo 300 italic` is a REAL face, not a synthesised one —
   measured 306.6px against 400's 308.6px on the same string, i.e. slightly
   narrower, which is what a genuine light cut does. (Synthesised weights get
   WIDER: 600 measures 345.4.) Do not go to 200; it also exists, but at 20px over
   a noise tile a 200 italic loses its thin strokes.

   COLOUR, one step off maximum on each ground:
   - paper: `--text-muted`, the exact colour `.hv4-lead` already uses on a light
     ground, so the aside now matches the section's own opening paragraph instead
     of outranking it at full `--dark-navy`. 5.7:1.
   - navy: `hsla(0,0%,100%,.85)`, which is the page's standing dark-ground lead
     tint. This reverses the note that used to live here — the argument was that a
     24px italic is thin-stroked enough to need full white where a 17.6px roman
     does not. At 20px/300 the block is smaller and lighter on purpose, so
     matching the lead tint is now the point rather than the problem. 12.3:1.

   LEADING 1.5 -> 1.6, matching `.hv4-lead`. The aside runs the full 1155px
   container as one column, and a long measure needs the looser leading more, not
   less, as the type gets smaller.

   The 1155px measure gets EASIER with this change, not harder. At 24px it was
   about 93 characters a line, which was the one real cost of running full width;
   at 20px/300 it is about 112, which sounds worse and is not, because the lighter
   smaller face is what the long measure was always short of. It stays either way
   — it was asked for twice. If these ever run long, narrow them; do not column
   them.

   *** NARROWED 09-30, WHICH IS THE ESCAPE HATCH THE LINE ABOVE LEFT OPEN. ***
   Jess: "capabilities are the unit of decision feels kind of tight on desktop."
   Measured at 1440 the paragraphs ran 131 / 121 / 113 characters a line — the
   note above budgeted for 127 at 24px and then 112 at 20px, and neither figure
   survived the copy getting longer. 131 is the longest measure on the page by
   32 characters; the section lead is 99 and body copy is 75.

   `44em` = 880px at the 20px cap = about 100 characters, so it keeps the block
   the widest
   paragraph on the page (110px wider than `.hv4-lead`'s 770px) while pulling it
   back inside the range the note above was already arguing was survivable.
   FULL WIDTH WAS ASKED FOR TWICE — "extended across the page not stopping
   halfway" and "not two columns one column across" — so this is the narrowest
   it should ever go, and it is still 76% of the container. Anything that reads
   as a column, and `columns: 2` in particular, is still the wrong answer.

   `em` AND NOT `rem`, AND IT IS STILL EARNING ITS KEEP AFTER THE SIZE WENT FLAT.
   The original argument was that a fixed length holds 880px while a clamped type
   size falls, so the measure gets LONGER as the page narrows — 117 characters at
   1200 against 100 at 1440, worst exactly where there is least room. The clamp is
   gone (see below) so that particular mechanism is, but the same arithmetic is
   what made the 10-01 size change safe: 44 × 17.6px = 774px, which is ~100
   characters, THE SAME COUNT THE 880px CAP WAS CHOSEN FOR. Had this been written
   as a fixed 880px it would now hold ~113 a line — back past the 112 this note
   already called borderline, and reached by doing nothing. A unit that tracks the
   type is the whole reason an instruction about measure survived a change to size.

   Side effect worth keeping: `#hv4-delivery`'s aside was running 127 / 128 / 9,
   i.e. a nine-character last line on its own. The reflow takes that out.

   The selector carries `.hv4-lead` only to outweigh `.hv4-section--navy
   .hv4-lead` up at line 118, which is (0,2,0) and was silently holding the .85
   tint against a bare `.hv4-aside p` at (0,1,1). Source order would have won it
   on a tie, but a tie that depends on two rules staying 1700 lines apart is not
   a thing to rely on.

   *** 1.1rem, WHICH IS `.hv4-lead`'s SIZE, AS OF 10-01. *** It was
   clamp(1.125rem, 1.4vw, 1.25rem) — 20px at 1440 against the section lead's
   17.6px — and that is backwards. An aside is a titled block INSIDE a section, so
   it is the section's own h2-and-lead pair one size down; the title is the part
   that shrinks, and the body copy matches the lead exactly. Running the aside's
   paragraphs 2.4px LARGER than the paragraph that opens the section made the
   subordinate block the loudest prose on the page.

   Asked for as "only fix the sizes" — step the title and body copy down, and let
   the text keep the full width. So `max-width: 44em` below is NOT part of this
   change and is not to be touched; it is 09-30's and has its own note.

   ⚠️ THE WIDTH MOVED ANYWAY, WHICH IS CORRECT AND IS NOT A BREACH OF THAT. 44em
   is a measure, not a length: at 17.6px it resolves to 774px where at 20px it was
   880px. The character count is what was specified and it is unchanged at ~100.
   774px also lands within 4px of `.hv4-lead`'s 770px, so the aside now runs the
   same measure as the section's own intro — which is the rule above, arrived at
   from the other direction. Do not "restore" 880px to defend the full-width
   instruction; that would widen the measure to ~113 characters, which is the
   thing 09-30 narrowed it to escape.

   The deleted clamp's own argument, kept because it explains the floor: the
   reference `.pa-bio-quote` is a flat 1.5rem in a narrow card, while this runs
   the full container, so a flat size measured 24 characters a line on a 330px
   column at 375. That end is still handled — the ≤767 block takes this to 1rem
   alongside `.hv4-lead` — it is just handled by a breakpoint now instead of a vw.

   NOT ITALIC as of 09-17: "let's try this not italic". The italic was the last
   thing still inherited from `.pa-bio-quote`, the practice-area pull quote this
   block borrowed its whole treatment from — and a pull quote is italic because
   it is someone's words lifted out of somewhere else. These asides are not
   quoted, they are the page speaking, and at up to 127 characters a line across
   the full 1155px container an italic face was also carrying more copy than an
   italic is built to carry.

   Everything else the reference gave it stays: the leo sans at 300, the muted
   colour, the 1.6 leading, the clamp. The block is still visibly its own thing —
   the serif subtitle above it does that work, which is exactly the argument the
   note further down makes for why the size did not also need to change. */
/* *** THE CAP IS GONE AT EVERY WIDTH, 10-01. DO NOT PUT A MEASURE BACK ON
   THESE PARAGRAPHS. ***

   "these two sections are supposed to stretch across. I don't want to say it
   again" — pointing at both asides, "Capabilities Are the Unit of Decision"
   and "Start Small, Build Momentum", on DESKTOP.

   THE COUNT IS FOUR NOW, and every previous fix was partial:
     "extended across the page not stopping halfway"
     "not two columns one column across"
     "these should go all the way across on tablet"   -> lifted ≤1024 only
     "same here all across"                           -> the other aside, same
     "supposed to stretch across"                     -> and here we are

   *** THE 09-30 NARROWING IS WHAT CAUSED THIS, AND IT IS NOT A LICENCE TO
   NARROW AGAIN. *** "capabilities are the unit of decision feels kind of
   tight on desktop" was answered with a 44em measure cap, and that was the
   wrong instrument: TIGHT WAS ABOUT THE LINE, NOT THE BLOCK. The real fix
   landed separately and is still in place — the type went from
   clamp(1.125rem,1.4vw,1.25rem)/500 to 1.1rem/300, which is what actually
   made a long measure readable. The cap was belt and braces on top of a fix
   that had already worked, and all it did was re-create the stopping-halfway
   that had been complained about twice before.

   If these ever genuinely run long again, the lever is the TYPE — size,
   weight or leading — or the copy. Not the width. `columns: 2` is still the
   wrong answer too.

   `none` RATHER THAN DELETING THE LINE, because .hv4-lead carries
   `max-width: 66.67%` at line 224 and that is what the aside would fall back
   to: 770px, which is narrower than the 774px cap being removed. Deleting
   this declaration would have left the bug and looked like the fix. */
.hv4-aside p.hv4-lead {
    max-width: none;
    font-family: "leo", sans-serif;
    font-size: 1.1rem;
    font-weight: 300;
    line-height: 1.6;
    color: var(--text-muted)
}

/* (0,3,1), which it needs: the rule above is (0,2,1) and would otherwise hold
   the light-ground colour on the navy ground on source order. */
.hv4-section--navy .hv4-aside p.hv4-lead {
    color: hsla(0, 0%, 100%, .85)
}

/* *** THIS BLOCK IS DELETED. *** It lifted the 44em cap at ≤1024 only, and
   the cap itself is now `none` at every width — see the rule above. A
   `max-width: none` inside a media query under a `max-width: none` outside
   one is a no-op that reads like a live breakpoint decision.

   Kept as a note because the measurement in it is the clearest statement of
   why the cap was wrong, and it generalises to the desktop case it was
   written to exclude:

   44em is a MEASURE, and a measure cap is only invisible while there is enough
   width for it to look chosen. Measured at 1024 the container's content box is
   890px and the cap holds the copy to 774px, so every paragraph in both asides
   stopped 116px short of a right edge that the condition cards directly above
   them run all the way out to. At 1440 the same cap leaves 33% of the container
   and reads as a set measure; at 1024 it leaves 13% and reads as a ragged edge.

   *** "READS AS A SET MEASURE" WAS THE PART THAT WAS WRONG. *** 33% of the
   container left empty is not read as a typographic choice by the person
   looking at the page; it is read as the paragraph stopping halfway, which is
   the phrase that had already been used about it. The note below said "The
   desktop cap is untouched. Do not widen it there to match this." That
   instruction is now reversed.

   It bites from roughly 870px up — below that the container's own content box
   is already narrower than 774px and `max-width` never applies, which is why
   this looked correct at 768 and only went wrong on the wider tablets. 1024 is
   the rung the rest of this page already calls tablet (see the .hv4-order
   block's note) and it covers the whole of the affected range.

   ⚠️ THIS DOES NOT REOPEN 09-30. That request was "capabilities are the unit of
   decision feels kind of tight ON DESKTOP", measured at 1440 where the uncapped
   line ran 131 characters. Uncapped at 1024 the container is 265px narrower, so
   the same copy runs about 115 — more than desktop's capped 100, and still well
   under the 131 that was actually complained about. The desktop cap is
   untouched. Do not widen it there to match this.

   — and 1024 is NOT a rung on this page's ladder either. The ladder is
   1199/1024/991/767/480 per the standard, so 1024 was defensible, but the
   whole block is moot now: there is no cap left at any width to lift. */

/* The paragraphs are .hv4-lead in the markup now, so there is nothing to
   declare: size, leading, measure and both per-ground colours all come from the
   lead rules the section's own opening paragraph uses.

   Two treatments were tried and both restated something the lead already says.
   It was 1.2rem for a while, which was the right fix when losing the old box
   left the aside indistinguishable from a paragraph of the lead, and the wrong
   one once it had a serif subtitle — the subtitle is a far stronger "this is its
   own block" than 2.4px of body copy, so the two together said it twice. Then it
   was .hv4-body at navy (and pure white on the navy ground), with colour doing
   the emphasis; that made it the one paragraph on the page brighter than the
   lead above it, which read as a correction rather than an aside.

   What separates the block is the title. That is the whole point of the request:
   heading and sub copy, same treatment, same spacing, and the only difference is
   that the heading is a subheadline.

   Only the trailing margin is overridden. Both asides are the last thing in
   their container, so the lead's 1.5rem would be section padding twice over. */
.hv4-aside p:last-child {
    margin-bottom: 0
}

/* A subtitle, on the model of #hv4-cost's "Nobody's Governing AI Usage" — leo
   serif, navy, sentence-scale.

   It was a 12px brandon-grotesque small-caps label, which is what it had been
   since it sat inside the old white tag bar. Out of the bar it was the wrong
   object: every other small-caps line on the page labels something small and
   subordinate — a chart axis, a beat, a column of conditions — and this one
   introduces four lines of the page's largest body copy. A label above a
   paragraph that outweighs it reads as a caption that lost its picture.

   26px at 1440 puts it a clear step under the section h2 and a clear step over
   the aside's own 17.6px lead, so the aside becomes a titled block in the flow
   rather than a labelled footnote.

   Leading, spacing and measure are `.hv4 h2`'s exactly — 1.2, 1.25rem under it,
   20em wide. The size is the only thing that differs, and that difference is the
   whole brief: this is a subheadline over its own lead, so the pair has to be
   built like the section's h2-and-lead pair and read as a smaller instance of
   it. .625rem under the title was the old value, from when it was a label
   sitting on a paragraph; a heading takes the heading's gap.

   Still a div, not a heading: `.hv4 h1..h4` sets leo 500 at (0,1,1) and would
   beat any single-class rule here. The font-family and weight are therefore
   declared rather than inherited — they are the same values that rule would
   have handed over.

   Navy is the light-ground value, for #hv4-delivery. #hv4-conditions is navy,
   so it is overridden below. */
/* Sized UP when the aside copy went to 24px italic. It was
   clamp(1.375rem, 1.8vw, 1.625rem) — 26px at 1440 — which was a clear step over
   a 17.6px lead and is only 2px over a 24px one. A title two pixels larger than
   the paragraph under it is not a hierarchy, it is a rounding error, and it is
   the exact thing Tim flagged on 9/9 about this block. 34px at 1440 restores the
   step, and the pair still sits well under the section h2's 43px.

   `.hv4-cond-card h3` at line ~2110 has a comment saying it borrows this clamp.
   It has its own copy of the old literal values, so it did not move with this
   and does not need to; that comment is what is now out of date, not the size.

   *** NOW ON THE STANDARD'S SUB-HEADING SCALE — clamp(1.5rem, 2.5vw, 2rem) IS
   .deliver-row h3 FROM styles.css, COPIED EXACTLY. *** 09-25, Jess: liminalarc.co
   "is our standard... I need this page to be consistent with the existing site."

   It was clamp(1.75rem, 2.4vw, 2.125rem), which is what caused the section
   headline above it to be pushed up off the site scale to compensate — see the
   inversion note on .hv4 h2. The standard already encodes the relationship this
   page kept getting wrong: .deliver-row h3 sits a deliberate 0.7rem under
   #deliver h2 at the top of the range, and 2.5vw is a STEEPER ramp than the h2's
   2.9vw is shallow, so the gap holds as the viewport narrows instead of closing.

   The reason this rule exists at all still holds at the new size. The aside copy
   under it is 24px italic; the old clamp(1.375rem, 1.8vw, 1.625rem) put the title
   at 26px, two pixels over its own paragraph, which is a rounding error rather
   than a hierarchy and is what Tim flagged on 9/9. 2rem is 32px at 1440 — still a
   clear step over 24px, and only 2px off the 34px this replaces. The fix was
   never in doubt, only its size.

   Its ≤767 partner is clamp(1.25rem, 5.5vw, 1.6rem) in the global responsive
   block at the foot of the file, and it is .deliver-row h3's ≤767 value copied
   the same way. Move the two together.

   ⚠️ 10-01: BOTH OF THOSE WERE BRIEFLY REPLACED BY ONE calc() RAMP — 20.6 at 375
   to 32.0 at 1440, i.e. the same two values on a continuous path — AND IT WAS
   REVERTED. It is not a tidier way of writing the same thing: a ramp through two
   anchors runs high between them, so the aside title grew on laptops along with
   everything else. The whole reasoning and the instruction are on .hv4 h2; the
   three headline rules were changed together and reverted together.

   ⚠️ The relationship to .hv4 h2 is what this rule is FOR. Changing one of them
   alone re-opens the inversion the note above is about. At 375 the ladder reads
   36.0 / 25.6 / 20.6 and at 1440 it reads 56.2 / 41.8 / 32.0 — check all three
   together, at both ends AND in the middle, after any edit. */
.hv4-aside-title {
    margin: 0 0 1.25rem;
    font-family: "leo", serif;
    font-size: clamp(1.5rem, 2.5vw, 2rem);
    font-weight: 500;
    line-height: 1.2;
    max-width: 20em;
    color: var(--dark-navy)
}

/* The title on the navy ground. White, matching what navy does to the light
   ground's navy everywhere else. It used to be --bright-green, which was the
   right call for a 12px label — small caps that small need colour to register at
   all — and the wrong one for a 26px serif subtitle, where green reads as a
   different kind of thing from the h2 above it instead of a smaller one.

   The paragraph has no override any more. It was pure white, beating
   `.hv4-section--navy .hv4-body p`'s .78 tint on source order alone, back when
   colour was the aside's only emphasis. As a lead it takes
   `.hv4-section--navy .hv4-lead`'s .85 — the same value as the lead at the top
   of the section, which is the point. */
.hv4-section--navy .hv4-aside-title {
    color: var(--white)
}

/* *** THE 4-UP BAND IS THE ONLY PLACE THESE TITLES ARE TIGHT, AND 1.45rem FLAT
   ONLY EVER FITTED AT 1440. *** 09-30.

   The note on `.hv4-condition h3` is right that 195px is the number every title
   in the set clears on two lines — and 195px is the measure at 1440 and NOWHERE
   ELSE in this band. `.la-container` is 80.2% of the viewport, so the measure
   falls straight out of it: 1440 -> 195, 1366 -> 180, 1280 -> 163, 1200 -> 147.
   "Organized Around Ownership" needs 190.2px at 23.2px type, so it went to three
   lines from 1439 down while the other three stayed on two, and the 48px rule
   under it dropped 28px below its neighbours' — measured at 1280, rules at
   y = 72 / 72 / 100 / 100 across the row. A row of four rules that do not line
   up is the near-miss from feedback_matched_pairs.md, one card wide.

   TWO CHANGES, AND THE PADDING IS THE ONE THAT MATTERS. `padding-right` is 20px
   of pure measure and it has never done its stated job — the numeral is 152px
   wide and the titles have always run over it on purpose, which is exactly the
   argument the base rule's own note makes for cutting it from 2.25rem to
   1.25rem. Cutting the last 1.25rem is the same fix one band further down.
   Without it, no type size fits at 1200: 147px of measure would need 16.5px
   type, under the 17px body copy on the same card.

   The clamp is then a straight 1.61vw — 23.2px at 1440 exactly, so the widest
   desktop is unchanged in size and only gains the 20px — falling to 19.3px at
   1200. Slack against the three-line threshold at each step: 1200 +8px,
   1280 +14, 1366 +20, 1440 +25, 1600 +57. Today's 1440 has FOUR pixels, which
   is why this looked settled and was not. The floor is inert here (it would
   need a 745px viewport) and exists only so the clamp is well-formed.

   Below 1200 the cards are 2-up and then 1-up, the measure roughly doubles, and
   the base rule's flat 1.45rem with its numeral padding is correct — this block
   deliberately does not reach there. */
@media(min-width: 1200px) {
    .hv4-condition h3 {
        font-size: clamp(1.2rem, 1.61vw, 1.45rem);
        padding-right: 0
    }
}

/* Breakpoints follow .transform-cards: 2-up between 768 and 1199, 1-up below,
   with the numeral scaling the same way. */
@media(max-width: 1199px)and (min-width: 768px) {
    .hv4-conditions {
        grid-template-columns: repeat(2, minmax(0, 1fr))
    }
}

@media(max-width: 991px) {
    /* Top 1.5rem -> 2.5rem, the same +16px as desktop and for the same reason —
       see the trough-not-crest note on the base rule. At 991 the wave box is
       38px, so the section's own ground above this padding runs 29.4px at the
       crest down to 7.1px at the trough; 2.5rem puts the trough at 47px. */
    .hv4-waveband-inner {
        padding: 2.5rem 0 3.5rem
    }

    /* Restated because the desktop rule it mirrors is an ID selector, and an ID
       beats a class no matter which media query the class sits in — the
       specificity trap this stylesheet has hit before. Without this line
       #hv4-conditions would keep its desktop value here while every other
       waveband edge dropped to 1.5rem. Symmetrical top and bottom.

       Deliberately NOT following desktop's 09-30 move to 3rem. That bought back
       clearance the bottom wave's curve was eating, and the wave scales with the
       viewport — at 991 there is proportionally less curve to pay for, and the
       band is short enough here that an extra 24px reads as a hole. */
    #hv4-conditions .hv4-waveband-inner {
        padding-bottom: 1.5rem
    }

    /* Same ID-beats-class trap as #hv4-conditions above, for #hv4-why's 7rem.
       ⚠️ BUT IT MUST NOT TAKE THE SHARED WAVEBAND VALUE. It did — 3.5rem, on the
       reasoning "that is what it would have inherited if it were not behind an
       ID" — and that is the one waveband edge the shared value cannot describe.

       ⚠️⚠️ #hv4-why HAS A WAVE AT ITS TOP AND NOTHING AT ITS BOTTOM. Every other
       user of the shared 3.5rem closes onto a wave, whose ~31px of ink pays for
       part of the join. This edge is flat — #cta-learning follows it straight
       on — so the padding is the whole join, exactly as the 7rem desktop note
       says ("there is no wave to subtract here, which is the one thing that
       makes this join simple").

       Derived the same way as the 7rem: this side + #cta-learning's own top,
       landing on the page's plain join. #cta-learning is shared across 21
       templates, so its side is a given, not a lever.

           991 → 768   ours 88 + theirs 40 = 128   plain join 128
           desktop     ours 112 + theirs 80 = 192  plain join 192

       At 3.5rem it was 56 + 40 = 96 against 128 — a quarter short, and the
       tightest join on the page again, which is the exact defect the desktop
       rule was written to fix. */
    #hv4-why .hv4-waveband-inner {
        padding-bottom: 5.5rem
    }

    .hv4-condition-num {
        font-size: 11rem
    }

}

@media(max-width: 767px) {
    /* A WAVE IS PART OF THE PADDING — BUT IT IS THE INK THAT COUNTS, NOT THE
       BOX, AND THE TARGET IS THE PHONE'S RHYTHM, NOT THE DESKTOP'S.

       ⚠️ THIS RULE WAS 4rem AND IT WAS DERIVED WRONG. The derivation was
       "restore the desktop total: 64 + 38 = 102". Two errors in one line:

       1. 38px is the wave's BOX. Sampled with getPointAtLength (never off the
          `d` attribute — Bézier control points lie off the curve), the colour
          boundary is at 20.3 of the 90-unit viewBox on both top waves and 73.2
          on #hv4-conditions' bottom wave. At a 38px box that is 29.4px of
          ground above the band's padding and 30.9px below it — not 38.
       2. 102 is the DESKTOP figure. Every plain section on this page runs 64px
          from its own edge to its first text at 375, and 96px at 1440. Landing
          a phone on 102 makes the waveband the one loose join on the page.

       What that produced, measured at 375 — last text of one section to first
       text of the next, across the whole page:

           plain joins   120 / 128 / 128
           wave joins    163 / 165 / 168      ← 40px adrift, three times over

       "feels like super inconsistent padding throughout" and, at the join below
       #hv4-delivery, "again feels like too big of gaps".

       2rem is the measured answer and it is also the desktop value, so the
       waveband is now symmetrical at every width. 31px of ink + 32px of padding
       = 63px of visible ground, against the page's own 64px. The three wave
       joins land at ~134 against 128 for the plain ones — 6px, which cannot be
       seen.

       ⚠️ THIS REVERSES THE 09-30 RAISE ("this spot might need more padding at
       the bottom"). That complaint was real: the value then was 1.5rem, which
       gives 31 + 24 = 55px of ground where the page gives 64, and the band did
       close up on its own last paragraph. 4rem overshot it by 31px. 2rem is
       between the two and is the only one of the three that lands on the
       standard. If this ever reads tight again, check the INK first.

       #hv4-why is in the selector list for the ID-beats-class reason given in
       the ≤991 block — its 7rem desktop bottom is an ID rule and would
       otherwise survive every media query below it. ⚠️ ITS BOTTOM IS PUT BACK
       IMMEDIATELY BELOW; only its TOP belongs in this rule. See the note there. */
    /* *** THE TOP IS 3rem AS OF 10-08 AND THE BOTTOM IS NOT. *** The +16px on
       the top is the same move made at every width — see the trough-not-crest
       note on the base rule. It matters more here than anywhere: on a phone the
       h2 runs the full column, so the curve's trough is directly over type
       rather than over empty ground beside it. 7.1px of ink + 48px of padding
       = 55px against the page's 64, where it was 39px.

       ⚠️ THE BOTTOM STAYS 2rem AND IT IS NOT AN OVERSIGHT. Both named sections
       override it anyway — #hv4-conditions in hv4-scaled-ai.css, #hv4-why
       immediately below — so the only section this value still reaches at its
       BOTTOM is #hv4-scaled, which has its own ≤767 rule in hv4-scaled-ai.css
       for the reason given there. Raising it here would be dead code. */
    .hv4-waveband-inner,
    #hv4-conditions .hv4-waveband-inner,
    #hv4-why .hv4-waveband-inner {
        padding-top: 3rem;
        padding-bottom: 2rem
    }

    /* ⚠️⚠️ #hv4-why's BOTTOM IS NOT A WAVEBAND EDGE AND MUST NOT TAKE THE
       WAVEBAND VALUE, 10-07: "also just noticed on mobile this is really tight",
       pointing at the ground under Learn More About Us.

       ⚠️ THE RULE ABOVE IS CORRECT AND ITS ARITHMETIC IS THE PROOF THIS ONE IS
       NEEDED. It reads "31px of ink + 32px of padding = 63px of visible ground,
       against the page's own 64px" — the 2rem is HALF the join and the wave is
       the other half. #hv4-why carries a wave at its TOP, so there it works. At
       its BOTTOM there is no wave at all; #cta-learning butts straight onto it.
       The 31px never arrives and the join is left at half size.

       ⚠️ #hv4-why IS THE ONLY SECTION ON THIS PAGE WITH A WAVE ON ONE EDGE AND
       NOT THE OTHER, which is why it is the only one that has to be split out.
       Anything else using .hv4-waveband-inner is waved top and bottom and the
       shared value describes both of its edges.

       Measured, same derivation as the desktop 7rem and the ≤991 5.5rem — this
       side plus #cta-learning's own top, against the page's plain join of 128:

           375   ours 80 + theirs 48 = 128    exact
           767   ours 80 + theirs 54 = 134    6px over, cannot be seen

       #cta-learning's top is not flat across the band (48 at 375, 54 at 767)
       because it is a shared component with its own ladder, and it runs on 21
       templates — its side is a given. 5rem is the value that lands the narrow
       end exactly; the 6px at the wide end is the same tolerance the rule above
       already accepts. Before 10-07 this was 32 + 48 = 80 against 128. */
    #hv4-why .hv4-waveband-inner {
        padding-bottom: 5rem
    }

    .hv4-conditions {
        grid-template-columns: minmax(0, 1fr)
    }

    /* "for mobile, let's make these boxes a tad tighter", 10-01. 28/24/24 →
       24/20/20, and then 10-08 → 20 on all four, following the desktop rule's
       "the top bottom and left right padding should be the same". A tad and no
       more: at a 330px card the side padding is the one figure here that costs
       measure, and 20px still leaves 290px, which is the same
       38-characters-a-line the card ran before.

       ONLY THE CARD MOVES. .hv4-condition .hv4-test carries its own 20/20/22
       and its note says in terms that the two are independent — the inner panel
       is sized to its own two-line question, not to the card around it. */
    .hv4-condition {
        padding: 1.25rem
    }

    .hv4-condition-num {
        font-size: 9rem
    }
}

/* ==========================================================================
   7. Correction — extract / enhance / exploit once, in the order the
      dependencies run in, with arrows between the cards. There used to be a
      second row repeating the same three cards backwards as the antipattern;
      it took an identical card treatment, which made it read as equally
      important, so it is a sentence in the closing paragraph now.

      THE CARDS ARE NOW THE "Three Step Cards" HANDOFF, 09-17. One white card
      three times — 1px edge, 6px coloured bottom bar, soft shadow — with all
      three cards' difference carried in the title: EXTRACT outlined in Brandon
      Grotesque, Enhance in Leo italic, Exploit on a green plate. They are no
      longer .exp-item derivatives and there is no artwork behind them.
   ========================================================================== */

/* No corner bloom here. An orange radial glow was built into the bottom-left of
   this band, copied from the /er page ("what if we did that here in the bottom
   left"), then asked for inside the three cards instead, then dropped
   altogether ("let's skip the bloom idea for now"). The section is styled by
   .hv4-section--teal and nothing else again. If it comes back: it needs
   `overflow: hidden; isolation: isolate` on the section and
   `position: relative; z-index: 1` on .la-container, because a positioned
   ::before at z-index 0 paints over ordinary block content. */

/* The grid is three 1fr tracks with the arrows positioned absolutely over the
   gaps, rather than the handoff's literal `1fr 48px 1fr 48px 1fr`. Same
   geometry, and it keeps the ≤1024 stacked behaviour (the arrows rotate and the
   gutters vanish) that a five-track grid would have had to rebuild.

   --hv4-order-gap IS THE ONLY LEVER ON CARD WIDTH and it moves three things at
   once, which is why it is a variable: the tracks are 1fr so the cards absorb
   whatever the gutters do not take, and both arrows position off it
   (100%/3 - gap/6 and 200%/3 + gap/6), so they stay centred in the gutters
   without a second number to keep in step. Never hard-code a card width here.

   5rem as of 09-18: "can we make these boxes a little less wide so there's more
   white space in between them and white space around the arrows." Both halves
   of that are the same number. At 1440 the grid is 1155px, so 48px gutters gave
   353px cards and 80px gutters give 332px — 21px off each card, and the gutter
   grows by 32.

   The arrow is the reason it went this far rather than to 4rem. It is 28px
   wide, so a 48px gutter left it 10px of air either side and the arrowhead was
   nearly touching the card edge; 80px leaves 26px, which is enough for the
   arrow to read as a space between two things rather than as a join. If this
   ever tightens again, 26px of clearance is the floor — below about 4.5rem the
   arrow starts crowding again.

   It was 2.75rem, then 3rem (the handoff's 48px gutter). The handoff's number
   is what is being departed from here, deliberately and by instruction. */
.hv4-order {
    --hv4-order-cols: 3;
    --hv4-order-gap: 5rem;
    position: relative;
    display: grid;
    grid-template-columns: repeat(var(--hv4-order-cols), 1fr);
    grid-auto-rows: 1fr;
    gap: var(--hv4-order-gap);
    margin-top: 3rem
}

/* Two arrows in the two gaps. These used to be pseudo-elements filled with a
   clip-path chevron: a solid green wedge, which is not an arrow this site
   draws anywhere. The brand arrow is a thin line with a small open head at
   1.5px — .ca-arrow on the homepage, .btn-arrow in the buttons,
   .insight-cta-arrow in the insight links all use the same 18x9 geometry, so
   this uses it too, as real inline SVG rather than a shape.

   They are absolutely positioned inside the grid container, which takes them
   out of grid flow so they do not become extra columns. They cannot live on
   the cards: .hv4-order-step already spends ::before on the swirl and ::after
   on the wash, and it is overflow: hidden, which would clip them anyway.

   With three 1fr tracks and gap g, a card is (100% - 2g)/3, so the first gap's
   centre is at 100%/3 - g/6 and the second at 200%/3 + g/6. */
/* --dark-teal (#0e4d64), 09-18: "let's make these the same color as the boxes
   so the teal." THIS IS THE CARDS' OWN GROUND VALUE — --order-ground is
   `14, 77, 100`, the same colour written as a triplet. The two are meant to
   match, so if the card ground is ever changed this has to move with it. It is
   not written as `rgb(var(--order-ground))` only because that variable is
   declared on .hv4-order-step and the arrows are siblings of the cards, not
   children, so it does not reach them.

   Fifth value in this slot: --bright-green (1.64:1 on white), --dark-green
   (3.87:1), --dark-navy (15.26:1), --light-green #4abd92 (2.34:1), now #0e4d64.

   THIS CLOSES A REAL FAILURE rather than trading one for another. #4abd92 was
   2.34:1 on the section's near-white ground against the 3:1 WCAG 1.4.11 asks of
   a graphical object, and it could not be argued away as decorative: the three
   cards are identical and there are no numerals any more, so these two arrows
   are the ONLY thing in the row carrying Extract → Enhance → Exploit. #0e4d64 is
   9.28:1 there. That flag is now closed and should come off any open list.

   The arrows also stop being a fourth colour in a row that already runs orange,
   yellow and green along the card edges. Matching the ground makes them read as
   the connective tissue between the boxes rather than as a fourth accent.

   Stroke stays 1.5px. Thickening the line would not have changed the old ratio
   anyway — 1.4.11 is a colour test, not a mass test.

   WHAT STILL DEPARTS FROM THE HANDOFF, which specifies a 28px Leo "→" glyph:
   it stays the brand's drawn arrow, not a typed one. The 18x9 line with a small
   open head at 1.5px is what .ca-arrow, .btn-arrow and .insight-cta-arrow all
   use; a serif glyph would be the one arrow on the site that is a character. The
   handoff's own instruction is to rebuild in the site's component library, and
   this is the component.

   What does come from the handoff is the SIZE: 28px of presence instead of 20,
   which is the difference between a connector and a tick mark. Same 18:9 ratio,
   so nothing distorts. */
.hv4-order-arrow {
    position: absolute;
    top: 50%;
    width: 28px;
    height: 14px;
    transform: translate(-50%, -50%);
    color: var(--dark-teal);
    pointer-events: none
}

/* fill:none on the <svg> does not reliably reach the polyline head, which then
   renders as a filled triangle — same note as .btn-arrow in styles.css. */
.hv4-order-arrow line,
.hv4-order-arrow polyline {
    fill: none;
    stroke: currentColor;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round
}

.hv4-order-arrow--1 {
    left: calc(100%/3 - var(--hv4-order-gap)/6)
}

.hv4-order-arrow--2 {
    left: calc(200%/3 + var(--hv4-order-gap)/6)
}

/* HISTORY — SUPERSEDED THE SAME DAY BY THE BLOCK IMMEDIATELY BELOW. This
   describes the WHITE cards, which lasted part of 09-17 before "roll the exploit
   enhance boxes back to when they were teal with the texture". None of it is the
   current state. It is kept in full because it is the only written record of the
   coloured version's grounds and ramp — the version that has now been restored
   was never committed — and because the reasoning for the 4rem title block, the
   three title faces and the sizes is still live.

   THREE WHITE CARDS, 09-17, from the "Three Step Cards" handoff. The card was
   identical three times and ALL the difference was in the title and the 6px bar
   under it.

   WHAT THIS REPLACED. The row has been, in order: three
   identical translucent tiles (3% white fill, 8% white border) that only read as
   cards on a dark section; then — when the section went white — three SOLID
   cards on --dark-teal / --blue / --dark-navy ("let's try a colored box for each
   one"), each carrying a slice of one swirl figure under a 115deg wash. Both of
   those are gone: the swirl ::before, the wash ::after, --order-ground, the
   overflow/isolation they needed, and the white-on-colour type they forced.

   WHY THE DARK VERSION HAD TO GO, beyond being asked to. Three saturated cards
   in a row on a white section are three grounds competing with each other, and
   the colour ramp (teal → blue → navy, "each step standing on the one before")
   was doing the same job as the arrows between them. The handoff keeps the ramp
   as a 6px bar and gives the type back its contrast: everything on these cards is
   now dark-on-white.

   --hv4-step is the per-card colour, set on the card and read by the bottom
   border AND by the title, so a card's bar and its word cannot drift apart. It is
   the only per-card value.

   BLUE / GREEN / GREEN, which is the handoff's ramp and not the page's section
   ramp: --bright-blue, --dark-green, --light-green. It runs cool to warm and
   light to mid, which reads as progress rather than as depth. The greens are back
   in the set because contrast is no longer the constraint — nothing sits ON these
   colours except the 60px plate on card three.

   THE BORDER IS BACK, and it is doing real work now. On the coloured version a
   hairline round three saturated blocks would have read as three buttons; a white
   card on a near-white textured section has no edge of its own, so the 1px
   #e3e7e9 and the shadow are what make it an object at all. #e3e7e9 is a literal
   rather than a token because the palette has no neutral at that value:
   --light-gray is #fbfbfb, a ground rather than an edge, and there is no border
   token at all. The handoff names #E3E7E9 and it is the only place it appears.

   Equal heights come from `align-self: stretch` against the grid's
   `grid-auto-rows: 1fr`, so the three bottom bars line up whatever the copy
   does. */
/* THE COLOURED CARDS ARE BACK, 09-17: "roll the exploit enhance boxes back to
   when they were teal with the texture" — with "keep the background we have",
   so #hv4-correction stays the white paper section it is now and only the three
   cards change. This reverses the white-card pass recorded in the block directly
   above, which lasted part of one day.

   READ THIS BEFORE CHANGING IT BACK EITHER WAY. The row has now been through
   three states and two of them are teal-ish, which is why the instruction needed
   interpreting:

     1. Three translucent tiles — 3% white fill, 8% white border — over a section
        that was itself --dark-teal. Committed at c7d4f544. They read as teal
        because the SECTION was teal; on today's white paper ground a 3% white
        fill is invisible and the green title measures 1.6:1. That state cannot
        be restored without also reverting the background, which was ruled out
        by name.
     2. Three SOLID cards on --dark-teal / --blue / --dark-navy, each carrying a
        slice of one swirl figure under a wash of its own ground. This is the one
        that lived on the white section, and it is the one being restored. It was
        never committed — it existed only in the working tree between c7d4f544
        and bbd40361 — so the grounds and the ramp come from the white-card
        comment that recorded them, and the swirl geometry is lifted verbatim
        from c7d4f544, which is the last commit that has it.
     3. The handoff's white cards, bbd40361. Superseded; see below.

   WHAT COMES BACK: the three grounds, the swirl ::before, the wash ::after, and
   the overflow/isolation/z-index scaffolding those two layers need.

   WHAT DOES NOT COME BACK, and is deliberate:
   - The 1px #e3e7e9 hairline and the card shadow. Those were added FOR the white
     card, which has no edge of its own on a near-white ground. A hairline round
     three saturated blocks reads as three buttons.
   - (SUPERSEDED — the 6px bottom bar is back, see below. This entry said it was
     the handoff's way of keeping a per-card colour ramp after the grounds went
     white, and that the ground was the ramp again so the bar said it twice. The
     grounds are identical now, so there is no ramp for it to duplicate and the
     bar is the only thing left telling the three cards apart.)
   - The hover shadow lift, which went with the shadow.
   - The card's size. The 09-17 "these feel too big and too tall" pass stays:
     same 4rem title block, same 1.5rem flex gap, same 2rem/1.75rem padding, same
     three title sizes. That was a separate instruction and nothing has withdrawn
     it.
   - The 4px radius. The old tiles were 8px; 4px is what the rest of the page
     settled on and there is no reason to fork it.

   ALL THREE ARE THE SAME TEAL, 09-17, second pass: "go back further when it was
   the teal boxes and all text was the same". The first attempt at this rollback
   restored the --dark-teal / --blue / --dark-navy RAMP, which is what the row
   looked like immediately before the white cards. That was one state too recent.
   The state being asked for is the one before it: THREE IDENTICAL CARDS. The
   ramp is gone, the three title faces are gone, and the three cards now differ
   only in which part of the swirl they show.

   WHAT THE RAMP WAS DOING, so it is not re-proposed: teal → blue → navy, "each
   step standing on the one before". The arrows between the cards already say
   that, and they say it without asking anyone to read three colours as a
   sequence. Identical cards make the arrows the only thing carrying the order,
   which is the right amount of work for them.

   --order-ground is still an RGB TRIPLE rather than a token, even though there
   is only one value now, because the wash below needs the same colour at four
   alphas and a var holding `#0e4d64` cannot be given one. It stays a variable so
   that if a per-card colour is ever wanted again it is a one-line change here
   rather than a rebuild of the wash. */
.hv4-order-step {
    --order-ground: 14, 77, 100;
    position: relative;
    display: flex;
    flex-direction: column;
    gap: .625rem;
    align-self: stretch;
    background: rgb(var(--order-ground));
    border-radius: 4px;
    border-bottom: 6px solid var(--order-accent);
    padding: 1.25rem 1.25rem 1.5rem;
    overflow: hidden;
    isolation: isolate
}

/* --order-accent, 09-17: "let's make the line a different color for each of the
   three. let's do bright blue, bright yellow, bright green."

   IT IS NOW SPENT IN ONE PLACE, the card's bottom edge. It was two — that rule
   plus a mark inside the card — and the inside mark went through four versions
   and was cut: "let's remove the number and line and only keep the line on the
   bottom of the card but make that thicker." The variable stays even at one use
   site, because it is what makes "the bottom line matches" literally true rather
   than three hexes kept in step by hand.

   3px -> 6px with that instruction. 6px is NOT a new number: it is the handoff's
   own bottom-bar weight, which this file had recorded as deliberately dropped.
   It was dropped because the cards had three different grounds then and the bar
   repeated what the ground already said. The grounds are identical now, so the
   bar is the ONLY thing distinguishing the three cards, and carrying that alone
   is what earns it the weight.

   THIS IS NOT THE RAMP COMING BACK. The teal→blue→navy GROUNDS were rolled back
   the same morning ("roll the exploit teal boxes back... the teal boxes and all
   text was the same") and must stay rolled back. What differs per card now is an
   accent on a shared ground, which is a different thing: the cards still read as
   three of the same object, and the colour is an edge on them rather than what
   they are made of. --order-ground is still one value for all three.

   The order was blue → yellow → green, the user's own, given in that order
   against Extract / Enhance / Exploit. EXTRACT IS NOW ORANGE, 09-17: "orange
   let's make this", against a crop of the first card's bottom edge.

   *** THE RUN IS GREEN → YELLOW → BLUE, 09-18, BY INSTRUCTION: "let's do green
   yellow blue for the order." ORANGE IS OUT OF THIS SECTION BY NAME: "no
   orange." It arrived on 09-17 ("orange let's make this") and left the next day.
   It is the one value in the palette that is NOT available here, and re-offering
   it reverses an explicit exclusion. ***

   THE ORDER TOOK FIVE PASSES AND ONLY ONE CONSTRAINT CAME OUT OF THEM. It ran
   blue → yellow → green, then orange → blue → green, then green → yellow →
   orange, then yellow → green → blue, then this. THE CONSTRAINT: #f46036 and
   #f5b939 may not sit on adjacent cards — at a glance they read as one warm
   colour twice, and telling the three cards apart is the bar's only job, because
   --order-ground is a single value for all three. Removing orange removes the
   constraint rather than working around it, so the hues are now free to be
   reordered on taste alone. Everything else in that sequence was preference and
   should not be reasoned about; just read the last line.

   CONTRAST, all three as graphical objects on the #0e4d64 ground, against
   1.4.11's 3:1:
     --bright-green  #6ce0b5  L .5984  ->  5.73:1
     --bright-yellow #f5b939  L .5441  ->  5.24:1
     --bright-blue   #3499e7  L .2929  ->  3.03:1
   ALL THREE PASS. That is new — every run that carried orange carried 2.90:1 and
   a standing client flag with it. Dropping orange closed it.

   BLUE IS THE TIGHT ONE NOW, at 3.03:1 — it clears by .03. If --order-ground is
   ever lightened, this is the value that fails first in this rule. Check it here
   before anywhere else on the page.

   *** SHORTFALL, CLIENT DECISION: orange is 2.90:1 and 1.4.11 asks 3:1. It is
   short by .10, and it replaces --bright-blue, which held the same slot at
   3.03:1 and passed by .03. The first card's edge has been the tightest of the
   three under both colours — if this ground is ever lightened, THIS is the value
   that fails first on the whole page, so check it here before anywhere else.
   Do not darken --bright-orange to close the gap: the token is used at full
   strength elsewhere on the site and the shortfall is .10, not a visible fault.
   The routes back are --bright-blue (3.03:1) or a lighter tint of orange. ***

   The bottom bar has the card above it but the section's WHITE ground below it,
   so each colour is judged twice. On white: green 2.34:1, yellow 1.77:1, blue
   3.06:1 — only blue clears 3:1 on that edge, and blue is the tight one on the
   teal edge. The two edges disagree about every colour in the palette and no
   single value satisfies both. They stay as they are, because the bar is not the
   only thing identifying a card — the title above it carries the same
   --order-accent — so on the white edge the bar is decoration, and the teal
   above is what gives it its shape. Recorded because the figures are real. */
.hv4-order .hv4-order-step:nth-child(1) {
    --order-accent: var(--bright-green)
}

.hv4-order .hv4-order-step:nth-child(2) {
    --order-accent: var(--bright-yellow)
}

.hv4-order .hv4-order-step:nth-child(3) {
    --order-accent: var(--bright-blue)
}

/* Swirl artwork. One box spanning three cards' worth of the figure, offset per
   card so each shows a different part of it and none repeats. Restored verbatim
   from c7d4f544 — every number below was argued once already and none of that
   argument has been reopened:

   The strip runs DOWN, not across, at every width — the box is one card wide and
   three cards tall, and cards 2 and 3 shift it up by one card plus a gap. It
   used to span the grid horizontally at desktop, which was "too zoomed in": the
   box came out 2298x322 at 1440, `cover` scaled the square source by the long
   axis, and each card's window showed 15% of the artwork's width by 7% of its
   height. At that crop a swirl has no curve left in it.

   The vertical strip at `cover` rendered the source at 571px and showed 62% by
   29%, which overshot the other way — "the texture here is too zoomed out"
   (09-16), because at 62% across a 356px card the lines sit close enough to read
   as a woven mesh rather than as a curve.

   `300%` fixes the scale in one number instead of letting the box derive it:
   300% of the box width is 1061px, so a card shows about 34% by 15% — roughly
   double the zoomed-out value and about half the zoomed-in one. Those two
   failures bracket this number; do not push past either.

   A percentage rather than a pixel figure so the crop holds as the cards narrow.
   The source is square, so 300% of the box width sizes both axes and the
   fraction of artwork in a card window is the same at every breakpoint.

   Not `100% 100%`: swirl.png is 2400x2400 and stretching a square source into a
   wide box squashes it into flat horizontal smears that read as a broken image.
   `.exp-item` on the production homepage gets away with `100% 100%` only because
   its grid is 4x2, so its box is about 3.8:1.

   The offsets are `top`, so they resolve against the card's own height — which
   is why `grid-auto-rows: 1fr` on .hv4-order matters to the artwork as well as
   to the bottom edges: if the three cards were different heights the figure
   would not line up across them.

   The filter tints the greyscale source toward the page's green and
   `mix-blend-mode: screen` keeps it additive, so it lightens the ground rather
   than printing on it. Screen is what makes one filter work over three different
   grounds. */
.hv4-order .hv4-order-step::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: calc(300% + 2*var(--hv4-order-gap));
    background: url("icons/swirl.png") center/300% no-repeat;
    filter: brightness(0) saturate(100%) invert(74%) sepia(18%) saturate(600%) hue-rotate(160deg) brightness(92%) contrast(85%);
    mix-blend-mode: screen;
    opacity: .25;
    pointer-events: none;
    z-index: 0
}

.hv4-order .hv4-order-step:nth-child(2)::before {
    top: calc(-1*(100% + var(--hv4-order-gap)))
}

.hv4-order .hv4-order-step:nth-child(3)::before {
    top: calc(-2*(100% + var(--hv4-order-gap)))
}

/* Washes the swirl back into the card so the copy keeps its contrast. 115deg,
   opaque at the top left where the title and the first words of the paragraph
   sit and clearing to nothing at the bottom right, so the artwork is strongest
   exactly where no glyph lands.

   The ramp is the card's OWN ground at four alphas rather than a fixed colour,
   which is what --order-ground being an RGB triple buys: a card's wash cannot
   drift away from its fill.

   THE RADIAL GLOW IS BACK now that all three cards are one colour. It is a
   lifted teal (rgba(20, 110, 140, .55)) blooming from the bottom right, and it
   is what stops the corner the ramp clears reading as a hole. It was dropped
   during the brief three-colour pass because a fixed teal bloom on a navy card
   is wrong; with one ground that objection is gone. It is deliberately NOT
   derived from --order-ground — it is a LIGHTER teal than the fill, which is the
   whole point of it, and rgba() cannot lighten. If a per-card ground is ever
   reintroduced, this line has to be re-examined first. */
.hv4-order .hv4-order-step::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(ellipse at bottom right, rgba(20, 110, 140, .55), transparent 55%), linear-gradient(115deg, rgb(var(--order-ground)) 0%, rgba(var(--order-ground), .9) 45%, rgba(var(--order-ground), .5) 75%, rgba(var(--order-ground), 0) 100%);
    pointer-events: none;
    z-index: 1
}

/* ------------------------------------------------------------------
   ONE TITLE TREATMENT, 09-17: "all text was the same".

   WHAT CAME OUT, and it came out as one piece because it only ever worked as one
   piece. The handoff's row gave each card its own face and size — card 1 outlined
   Brandon Grotesque at 42px with a 2.5px stroke, card 2 Leo italic at 53px, card
   3 Leo 500 at 47px on a solid plate — and that in turn REQUIRED a fixed
   `height: 4rem` title box with `align-items: flex-end`, because three faces at
   three sizes otherwise sit on three baselines and the row looks broken. Titles,
   sizes, plate, stroke, the @supports fallback for the stroke, the title box, and
   the six flat size overrides in the 1024 and 767 media queries: all gone
   together. Restoring any one of them means restoring the box too.

   THE MARKUP CHANGED WITH IT. The <span> inside card 3's h3 existed only to be
   the plate, and it is deleted from template-homepage-v4.php — the three h3s are
   now identical elements, not three elements that happen to be styled the same.

   WHAT REPLACES IT is the pre-handoff rule, from c7d4f544: one size for all three
   at clamp(1.375rem, 1.8vw, 1.625rem) — 26px at 1440, 22px at 992 and below —
   which is deliberately NOT a value of its own. It is exactly .hv4-aside-title's
   clamp, so the three card titles match the page's other subheadline. It also
   stays under .hv4-gov h3 (30px at 1440), which is right: a title inside a 3-up
   card should not outsize the page's section-level h3s.

   WHITE, ITALIC, ONE STEP BIGGER — 09-17: "let's make extract enhance text white
   and a bit bigger maybe even italic". This REPLACES the --bright-green accent
   and the inversion it was half of. That pairing put the title in green and
   stepped the body copy UP to .92 white, so the sentence was the brightest thing
   in the card and the word above it read as a label. The title is now the
   brightest thing again, which is the ordinary order, so the inversion argument
   is closed rather than inverted a second time.

   The body copy stays at .92 white. Title and body are now within one step of
   each other in colour, so the whole hierarchy is carried by SIZE and by the
   italic — 28px italic 500 over 16px roman 400. That is deliberate: a white
   title over .78 body would have been the same separation stated twice.

   ITALIC WAS TRIED AND REVERSED, same day: "maybe even italic", then "remove
   italic from extract text". It was leo 500 italic, exactly .hv4-pullquote's
   treatment in Who We Are, so it was the page's existing display italic rather
   than a new face — and it still came out. Three roman card titles in a row are
   labels; three italic ones read as three quotations. Do not re-offer it. The
   `font-style` line is deleted rather than set to `normal`, because roman is
   what the h3 inherits anyway.

   SIZE: clamp(1.375rem, 1.8vw, 1.625rem) -> clamp(1.4375rem, 1.95vw, 1.75rem).
   26px -> 28px at 1440, 22px -> 23px at the floor. It is capped at 1.75rem
   specifically to stay under .hv4-gov h3 (clamp(1.5rem, 2vw, 1.875rem), 30px at
   1440): a title inside a 3-up card should not outsize the page's section-level
   h3s. The slope is 1.95vw rather than gov's 2vw for the same reason — matching
   the slope would have made the two identical at every width below 1400.

   Contrast improves with the change: white on #0e4d64 is 12.0:1, against the
   green's 5.73:1. At 28px it is WCAG large text either way.

   *** THE CARD CARRIES NOTHING ABOVE THE TITLE. DO NOT ADD A MARK HERE. ***
   09-17: "let's remove the number and line and only keep the line on the bottom
   of the card but make that thicker." FOUR treatments were built and reversed in
   this one spot on this one day, in this order:

     1. a fixed 3rem mark UNDER the word — fixed on purpose, because Extract,
        Enhance and Exploit are three different lengths and three content-width
        marks would break the matched set (feedback_matched_pairs);
     2. the same rule at 1px instead of 2px ("very thick");
     3. an italic counter + a rule running right, all on the TITLE'S OWN LINE —
        reversed because "01 Extract ————" reads as one phrase;
     4. the same pair on their OWN ROW above the title, matching the reference
        screenshot — reversed too.

   The card's bottom edge was present through all four and is what survives. The
   through-line of the reversals is that a second accent mark inside the card was
   always competing with it; the sequence is now carried by the bottom bar alone.
   The counter machinery (`counter-reset`, `counter-increment`, the `::before`)
   is DELETED, not just hidden, so reinstating numerals is a decision. */
.hv4-order-step h3 {
    position: relative;
    z-index: 2;
    margin: 0;
    font-size: clamp(1.4375rem, 1.95vw, 1.75rem);
    line-height: 1.25;
    color: var(--white)
}

/* THE RULE USED TO BE A SEPARATE `h3::after`, TWICE, AND IT IS NOW THE ::before'S
   BACKGROUND. Both earlier versions are gone and neither should come back:

     1. A FIXED 3rem mark UNDER the word. Fixed on purpose — Extract, Enhance and
        Exploit are three different lengths, and three content-width marks would
        have drawn three different rules in a matched set (feedback_matched_pairs).
     2. A `flex: 1` rule on the TITLE'S OWN LINE, running from the word to the
        card's inner edge. Reversed by "I wanted the number and line above the
        text".

   Running right is still how the matched-set problem is solved, but from the
   other end: every rule now BEGINS at 44px and ENDS at the card's inner edge, so
   all three sit on the same two verticals and no word's length is involved at
   all. 1px is kept from "the extract enhance lines under the text are very
   thick"; at this length 2px would compete with the card's own 3px bottom edge.

   The card's `gap: .625rem` sets the distance from title to paragraph, and the
   ::before's own `margin-bottom: .625rem` matches it, so the numeral row, the
   title and the body are three evenly spaced beats. */

/* Body copy. Back to white on the coloured cards, at .92 rather than the .78 the
   dark sections use for body copy. Stated as an alpha rather than a hex so it
   stays in the page's existing idiom (.78 body, .85 lead, 1 for the closing
   line); this sits one step below full white.

   .92 white composites to roughly #ecf1f3 over any of the three grounds, so the
   floor is the blue card at about 6.0:1 and the navy card runs near 14:1. All
   three clear AA for normal text, which is the bar that matters for 16px copy.
   It was --text-muted at 5.61:1 on the white card. */
.hv4-order-step p {
    position: relative;
    z-index: 2;
    font-family: "leo", sans-serif;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    color: hsla(0, 0%, 100%, .92);
    margin: 0;
    text-wrap: pretty
}

/* Closing line. IT IS NOW THE ASIDE'S TYPE, 09-17: "let's have this text match
   this style", pointing at .hv4-aside p.hv4-lead ("Capabilities Are the Unit of
   Decision"). Every value below is that rule's — leo 300, the same clamp, 1.6
   leading, --text-muted, and no measure cap — so the two are one treatment and
   should be changed together. The <em> on the last sentence stays.

   FULL WIDTH IS THE POINT OF THE MATCH. This was capped at 43rem (688px, 59.6%
   of the 1155px container), which was itself a widening from the handoff's 62ch
   after Tim's "it should be like 60%, not 50, 40%". The aside answers the same
   note by running the whole container, and this paragraph is the same kind of
   object: one block of body copy under a graphic, not a column of a grid. The
   aside's own rule carries the reasoning about why a 1155px measure is
   acceptable for short blocks — see .hv4-aside p.hv4-lead.

   WHAT THE MATCH COSTS, recorded because it was deliberate before: this was
   --dark-navy rather than --text-muted, on the grounds that it is the section's
   conclusion and carries the antipattern that used to be a row of cards. It goes
   muted to match. Still 5.61:1 and still the last thing in the section; the
   emphasis now comes from position and from the italic close, not from ink. */
/* *** 1.1rem, 10-01. THE PAIR HAD COME APART AND THIS IS THE HALF THAT WAS
   LEFT BEHIND. *** "font sizing feels inconsistent here", pointing at this
   section.

   The note above says these two rules "are one treatment and should be
   changed together". On 10-01 the aside was stepped down from
   clamp(1.125rem, 1.4vw, 1.25rem)/500 to 1.1rem/300 — and this rule, which
   exists ONLY to copy the aside, kept the clamp. So the section measured:

     h2                    41.8px / 500
     opening lead          17.6px / 400
     card titles           28px   / 500
     card body             16px   / 400
     THIS closing line     20px   / 300   <- the largest body copy in the
                                             section, and the last and most
                                             subordinate thing in it

   The reasoning is the aside's, verbatim, because it is the same mistake: a
   closing line is the section's lead one beat later, not a louder version of
   it. Running it 2.4px larger than the paragraph that OPENS the section made
   the quietest block the loudest prose. 1.1rem is `.hv4-lead`'s size, so the
   two paragraphs that bracket this section now measure the same and only the
   weight, the colour and the italic close separate them.

   ⚠️ IF THE ASIDE'S SIZE EVER MOVES AGAIN, MOVE THIS WITH IT. That
   instruction was already in the note above and was still missed, so here it
   is on the declaration itself. The two rules are 900 lines apart; grep
   `hv4-order-close` and `.hv4-aside p.hv4-lead` together. */
.hv4-order-close {
    margin: 3rem 0 0;
    max-width: none;
    font-family: "leo", sans-serif;
    font-size: 1.1rem;
    font-weight: 300;
    line-height: 1.6;
    color: var(--text-muted);
    text-wrap: pretty
}

/* *** 991, WHICH IS WHERE EVERY THREE-ACROSS GRID ON THIS SITE COLLAPSES. ***
   Moved off 1024 on 10-13. "CONSISTENCY is the goal... if a 2 column changes to
   1 column, that change needs to happen to all 2 columns at the same spot" and,
   on being shown the survey below, "for an odd number I'd rather go straight
   to 1."

   THE WHOLE SITE'S THREE-ACROSS BEHAVIOUR, surveyed before moving it:

     .fa-case-results    case study results    3 -> 1 at 991
     #ve-tool .ve-whatif-cells                 3 -> 1 at 991
     .cn-pillar-grid     consulting pillars    3 -> 1 at 991
     .hv4-rg             readiness gap (ours)  3 -> 1 at 991
     .pa-cap-grid        practice-area caps    3 -> 2 at 1024 -> 1 at 767

   Four of the five go straight to one, and all four do it at 991. Going
   straight to one is confirmed right — two tracks orphan the third card, which
   is the defect .pa-cap-grid actually has. So the BEHAVIOUR here was always
   correct and only the WIDTH was wrong.

   ⚠️ THE 1024 WAS BORROWED FROM A COMPONENT THAT BEHAVES DIFFERENTLY, AND THAT
   IS WHY IT CAME OUT. It was matched to #pa-expertise by instruction on 09-16
   so "the two sections agree on where tablet ends" — but #pa-expertise is
   .pa-cap-grid, the one grid in the table that does NOT go straight to one. At
   1024 it steps to TWO and only reaches one column at 767. The number was taken
   from a rung that means something else there, so it never actually put this
   section in step with anything.

   THE SQUEEZE ARGUMENT THAT DEFENDED 1024 DOES NOT SURVIVE ARITHMETIC. The old
   note here said three tracks "come out 268px each" at 1024 and the paragraphs
   run four lines. Measured on the shipped page, with --hv4-order-gap at its
   current 5rem:

              container   track   (gap takes 160 of it)
     1199        1043      294
     1025         892      244   <- ALREADY THE SHIPPED FLOOR
     1024         891      244   <- was stacked, now three across
      992         863      234   <- the new floor

   244px already ships at 1025. The new band only reaches 234, ten pixels and
   4% below a width that has been live for weeks, so there is no new squeeze to
   pay for.

   ⚠️ AND DO NOT PAY FOR IT BY TIGHTENING --hv4-order-gap. That was built and
   discarded in the same pass: 3.5rem gives 250px at 992, matching .cn-pillar-
   grid exactly, but 1fr tracks absorb whatever the gutter drops, so it also
   WIDENS the cards at 1199 from 294 to 310 — and .la-container's padding jumps
   at 1200 (77.9 -> 118.8), which already costs 294 -> 267 across that rung.
   A wider 1199 turns that into 310 -> 267. Tightening the gutter to fix a
   10px problem at the bottom of the band creates a 43px one at the top.

   Two tracks would orphan the third card, so it goes straight to one. .hv4-steps
   is a different family and stays at 767: it is a grid of six equal cards, and
   card grids turn at 767 site-wide (see the map at section 5). */
@media(max-width: 991px) {
    /* Gap stays wide enough for the arrows, which are what say "order" now
       that the row label is gone.

       3.5rem, up from 2.25rem on 09-30: "I feel like I want a little more
       spacing between the boxes and arrows on mobile. this feels tight."
       Rotated, the arrow is 28px on this axis, so 2.25rem (36px) left it 4px
       of clear ground at each end — it read as touching both cards. 3.5rem
       gives 14px a side, half the desktop clearance (5rem gap, 28px arrow,
       26px a side) on a layout where everything else is tighter too.

       The two arrow offsets below are written in terms of this variable and
       stay centred in their gaps on their own; nothing else needs changing. */
    .hv4-order {
        --hv4-order-cols: 1;
        --hv4-order-gap: 3.5rem
    }

    /* Same third/two-third math as the desktop arrows, on the other axis: the
       rows are grid-auto-rows: 1fr, so they are equal heights too. */
    .hv4-order-arrow {
        left: 50%;
        transform: translate(-50%, -50%) rotate(90deg)
    }

    .hv4-order-arrow--1 {
        top: calc(100%/3 - var(--hv4-order-gap)/6)
    }

    .hv4-order-arrow--2 {
        top: calc(200%/3 + var(--hv4-order-gap)/6)
    }

    /* No artwork override here. The swirl strip used to be rebuilt in this block
       because desktop spanned the grid HORIZONTALLY and the stacked layout had
       to undo it. The strip now runs down at every width and its offsets are
       expressed against the card's own height, so one set of rules covers both
       layouts.

       NO TITLE OVERRIDES EITHER, 09-17. This block used to carry flat copies of
       three per-card font sizes, which silently undid every desktop resize until
       they were caught stale mid-edit. With one title treatment there is one
       clamp, and a clamp needs no breakpoint to restate it. Do not reintroduce
       sizes here. */
}

/* Phones. Only the box tightens now; the title's clamp already bottoms out at
   22px and needs no help. The three title overrides that used to sit here went
   with the three title treatments. */
@media(max-width: 767px) {
    /* .hv4-order-close has no override here any more. It used to drop to 1rem;
       it now takes the aside's clamp, whose 1.125rem floor IS the phone size,
       and the aside has no phone override either. One treatment, one place. */
    .hv4-order-step {
        padding: 1.25rem 1rem 1.25rem
    }
}

/* ==========================================================================
   8. Delivery — one slice. The step treatment from the "One Slice Section"
      handoff, on the handoff's navy ground, with the handoff's trimmed copy.

      THE FIGURE, second version, drawn from the deck slide rather than from the
      handoff. The handoff's version bled four estate bands off the left edge of
      the viewport with the cut inside them; it was built, tried left, mirrored
      right, and pulled ("let's remove the slice visual for now"). This one is
      four discrete labelled cards with one solid column overlapping their right
      half, standing slightly proud top and bottom, captioned in italic
      underneath. Nothing bleeds — it is a grid column beside the steps.

      The step list stopped being orange outlined circles down a single indent.
      Those numerals were the only orange on the page and they read as a UI
      control; these are leo italic in the section's green, over a hairline that
      the label shares, which is the same "beat of one argument" hairline idiom
      the rest of the page uses (see feedback_rules_and_hairlines).

      The headline and the lead have no rules of their own here at all — they
      are `.hv4 h2` and `.hv4-lead` doing what they do in every other section.
      The handoff's flat 38px h2, 20ch measure and -.01em tracking were authored
      for a 1120px frame with no other headline to answer to; on the page they
      would have made this the one h2 smaller and narrower than the rest.
   ========================================================================== */

/* No padding override. This section used to take the handoff's 72/68px, which
   undercut the page's shared 6rem by 24 and 28px and was the first thing that
   made the band feel tight ("see how this has breathing room? I want that for
   the one slice section" — against #hv4-correction, which takes the 6rem). The
   handoff's padding was authored for a 1120px frame holding nothing but this
   section; on the page it has neighbours to keep time with. */

/* ══ THE FIGURE MOVED UP, 09-17 ═══════════════════════════════════════════════
   "let's move the graphic up to the right of the headline", then "let's make the
   graphic smaller", then "let's make the slice section and this match" pointing
   at /asset-modernization/'s numbered steps.

   WHAT .hv4-slice-grid WAS, because it is deleted and the class is gone from the
   markup: one 272px + 1fr row holding the figure and the six steps side by side,
   at a 6.5rem gutter, with `align-items: stretch` so the figure — which has no
   intrinsic height — took the steps' 395px and the bands divided it. The figure
   was second in the document with `order: -1` pulling it left.

   THAT ARRANGEMENT IS WHAT MADE THE STEPS NOT MATCH THE REFERENCE. Everything
   else about them already did: green italic serif numeral, brandon tracked caps
   label, 1px hairline under both, leo serif body in --text-muted. The single
   difference was the measure — the reference runs two columns across the WHOLE
   container, and these were two columns inside a 779px track with a picture
   beside them. Moving the figure out is the whole of the "match"; not one type
   value below changed. Do not go looking for one.

   SO THE STEPS NOW GET THE FULL 1155px and the head row is headline + lead on
   the left, figure on the right.

   THE SECOND TRACK IS CONDITIONAL, 09-17 — "let's remove the one slice graphic
   for now". The figure is commented out of the template, not deleted, so the
   track is written with :has() rather than removed: with no .hv4-slice-fig in
   the row there is one full-width column and no gap, and putting the markup
   back restores the second track with no edit here. A fixed second track with
   nothing in it would have cost the headline and lead 312px of measure.

   THE FIGURE CAME BACK 09-18 AT 420px, not 232. The width and the 64px gutter
   are the handoff's, and the handoff says match them exactly. At 1440 the
   container content is 1155px, so the left track is 1155 − 420 − 64 = 671px. */
.hv4-delivery-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 5rem;
    align-items: start;
    margin-bottom: 1.5rem
}

/* THE LEAD-IN IS CONDITIONAL TOO, and for the same reason the track is —
   "this feels kind of low", 09-17, about the first row of rules sitting too far
   down the page. It was a flat 3.5rem, which was correct WHILE THE FIGURE WAS
   THERE: the figure's bottom edge is a hard horizontal line and it needs more
   clearance than type does. With the figure gone the row ends on a paragraph's
   last baseline, and 56px + the lead's own 24px bottom margin put 80px between
   the last sentence and the first blue rule. 1.5rem makes that 48px, which is
   the reference block's own heading-to-grid distance. Both values live with the
   track they belong to, so the figure coming back restores its own spacing. */
.hv4-delivery-head:has(.hv4-slice-fig) {
    grid-template-columns: minmax(0, 1fr) 420px;
    column-gap: 4rem;
    margin-bottom: 3.5rem
}

/* "we can maybe wrap that body copy to better fit the graphic so they are two
   column", 09-18. .hv4-lead is `max-width: 66.67%` everywhere on this page,
   which is right when the lead has the whole 1155px container to sit in — it
   holds the measure to ~770px. In a 671px track it holds it to 447px, which
   sets the lead at about 45 characters and throws it onto five lines beside a
   three-line headline. The percentage is doing its job against the wrong
   number. Released here so the lead runs the track it was given; the precedent
   is #hv4-cost's lead, released for the same reason. */
.hv4-delivery-head:has(.hv4-slice-fig) .hv4-lead {
    max-width: none
}

/* `align-items: start`, NOT stretch, and this is the one thing that has to be
   understood before touching the figure. Stretched against a 158px intro the
   figure would be 158px tall; the old stretch worked only because it was
   opposite a 395px list. The figure is given an explicit height below instead,
   which is the same answer the ≤991 stacked case has always used. */
.hv4-delivery-intro {
    min-width: 0
}

/* The six steps. Two columns, three rows, ACROSS THE WHOLE CONTAINER as of
   09-17 — the figure is no longer beside them, so the 779px track is now 1155px
   and the columns go 337px to 493px each, about 77 characters of 15px serif.
   That is the /asset-modernization/ measure, which is the thing being matched.

   77 characters is above the 66 a text column is usually held to, and it is
   accepted here rather than fixed with a max-width: these are six short bodies
   of one or two sentences, not a reading column. The reference page runs the
   same width on the same kind of block. If any of these bodies ever grows past
   three lines, revisit this before revisiting the type.

   104px between the columns. It was asked for four times, 56 → 72 → 88 → 104,
   back when it also had to equal the gutter to the figure so the eye did not
   group columns 1 and 2 against the picture. That constraint is gone with the
   figure, but the number stays: it is now simply the widest gutter on the page
   and it is what keeps two 493px columns from reading as one 1090px block.

   The 3rem row gap and the 3rem lead-in are #hv4-correction's numbers, not new
   ones: .hv4-order is `margin-top: 3rem` with a 2.75rem gap, and its cards buy
   another 20px of air apiece from their own padding. These steps have no card
   to pad, so the air has to come from the gaps — hence 3rem rather than 2.75.

   The lead-in moved here from the deleted .hv4-slice-grid, and it is the head
   row's `margin-bottom` that now sets the distance from the lead — 3.5rem, a
   little more than 3rem, because the figure's bottom edge is a hard horizontal
   line and it needs more clearance than a paragraph's last baseline does.

   What is deliberately NOT borrowed is the card: no fill, no border, no radius.
   Boxing six steps two sections after three bordered cards would make the two
   rows read as the same kind of object.

   ══ THE CORE CAPABILITIES PATTERN IS OUT, 09-21 ══════════════════════════════
   "This needs to be some kind of ordered list since there is a sequence" /
   "for this one, let's do the number style like this instead:" / "what if even
   we did something like how we have for our appraoch and it was like one slice".

   What was here from 09-17 was #pa-capabilities from /practice-area/product-
   and-strategy/: a 3px --bright-blue rule ACROSS THE TOP of each item, 1.05rem
   bright-blue tracked caps 1.75rem under it, body beneath, and no numerals.
   All of it is gone. The pattern now is .fa-case-approach-* from
   /facades/asset-modernization/ (styles.css:4971-5019) — the "Our Approach"
   block in the screenshots — taken value for value:

     numeral   "leo" italic 500, 1.875rem/1, -0.01em     (.fa-case-approach-num)
     label     brandon 700, .85rem, .22em, uppercase     (.fa-case-approach-label)
     head      flex, baseline, 1rem gap, 12px pad-bottom, 1px hairline under
     body      14px under the head                       (the item's flex gap)

   THE 3px RULE BECAME A 1px HAIRLINE, AND THAT IS THE POINT OF THE CHANGE, not
   a side effect of copying the block. A 3px coloured rule opening each item is
   the dividing rule of feedback_rules_and_hairlines — it says "six separate
   things", and six of them said it six times. These are not six separate
   things. They are six steps of one sequence, which is exactly what "there is a
   sequence" is asking to see. A 1px neutral hairline tying a numeral to its
   label inside one step is that same file's connecting rule, and the numerals
   carry the order across the six. The old rules were working against the thing
   the block is for.

   THE NUMERALS ARE BACK, AND THAT REVERSES "ditch the numbers" (09-17). Asked
   for by name and shown in two screenshots, so it is a reversal by instruction
   and not a regression — but it IS a reversal, and the note that used to stand
   here said not to do it without one. They are a CSS counter on the <ol> again
   rather than typed into the markup: the list is already in document order,
   `decimal-leading-zero` gives 01-06, and generated content stays out of the
   accessibility tree, which is right because the <ol> already carries the
   sequence for a screen reader. Moving a step reorders the numerals with it.

   *** THE KNOWN CONTRAST SHORTFALL IS CLOSED BY THIS, and not by changing a
   colour to fix it. *** --bright-blue was 3.06:1 on this near-white ground and
   was failing 1.4.3 on the labels, because 16.8px bold is normal text and asks
   4.5:1. The labels are --text-muted now, the reference's own label colour, at
   5.9:1. The flag is closed by the rebuild.

   THE NUMERALS ARE --dark-green #259271, 09-21: "also let's try the numbers as
   green 359271" / "259271*". Built --bright-blue first, on the reading that the
   shape was what was being asked for and the section's colour should stay put;
   overruled by the hex. *** THIS MEANS THERE IS NO BRIGHT BLUE LEFT IN THE
   SECTION AT ALL, which does reverse "the text and lines are both the bright
   blue in the secondary pallet" (09-17). *** It is a reversal by instruction,
   but the earlier instruction is not written down anywhere on the page any
   more, so it is written down here.

   --dark-green measures 3.73:1 on this ground. That clears 1.4.3 at this size
   and only at this size: 30px is WCAG large text (24px normal / 18.66px bold),
   which asks 3:1. IF THE NUMERAL IS EVER SHRUNK BELOW 24px IT FAILS — the
   threshold is 4.5:1 below that and green does not have it. The size and the
   colour are a pair here.

   THE REFERENCE'S OWN NUMERALS ARE #4abd92 AND THAT IS STILL NOT COPIED.
   --light-green is 2.25:1 here — it fails even the 3:1 large-text floor. The
   green that was asked for is the dark one, which is also the one that passes;
   do not "correct" it to the reference's lighter green to match /facades/.

   *** TWO COLUMNS, 09-21: "the spacing all around feels awkard here. what if it
   was 2 column?" *** This reverses the three-column argument below, which is
   kept because the height cost is real and somebody will want it back.

   WHAT WAS ACTUALLY AWKWARD, and it was not the column count on its own. At
   three columns the tracks are 283–347px, which is narrow enough that two of the
   six labels wrap and four do not. The fix for that was min-height: 60px on the
   head — it lines the six hairlines up, but it does it by holding ~17px of empty
   air above the hairline on the four labels that DON'T wrap. So four of the six
   heads had a gap between the cap and the rule that nothing in them explained,
   and the rag of the bodies underneath moved with it. Two columns make the
   tracks ~550px, no label wraps at all, and the min-height stops being needed —
   so the spacing is even because every head is genuinely the same, not because
   one is being propped up to match another.

   *** THE min-height IS NOW SCOPED TO 768–991 ONLY. *** That is the one band
   left where the tracks (284–314px) are narrow enough to wrap a label. See the
   note on .hv4-step-head. If the column count here changes again, that media
   query has to be re-derived — the two are one decision written in two places.

   THE HEIGHT IS PAID FOR AND IT IS NOT FREE: three rows instead of two. The
   min-height coming off gives ~51px back, so it is not the full 185px the note
   below predicted, but the block is still taller than it was. That is a
   deliberate trade against the standing "make the page shorter" brief, made
   because even spacing was asked for by name.

   THE SUPERSEDED ARGUMENT, kept: the reference has four items and this has six,
   so two columns is three rows and roughly 185px taller. Three columns reads
   01 02 03 / 04 05 06, which is the sequence in reading order — two columns
   reads 01 02 / 03 04 / 05 06, which is still reading order but pairs steps that
   are not pairs. The .85rem label is what made three columns possible at all —
   at 1.05rem the caps wrapped to two lines in a 337px column. */
.hv4-steps {
    list-style: none;
    padding: 0;
    margin: 0;
    counter-reset: hv4s;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 4rem;
    row-gap: 3rem
}

/* `list-style` has to be reset on the li, not the ol: the theme sets
   list-style-type on the li itself, which beats anything declared on the
   parent. That also reclaims the marker's indent. */
.hv4-steps li {
    list-style: none;
    counter-increment: hv4s
}

/* The head row. `align-items: baseline` is what sits a 30px numeral and a
   13.6px cap on one line without either being centred against the other, and it
   is why this is a flex row rather than two inline spans.

   The hairline is rgba(6,41,53,.14), the page's light-ground value, NOT the
   reference's rgba(14,77,100,.18). The two are within a point of each other and
   the house value is a standing rule (feedback_rules_and_hairlines); a second
   near-identical hairline colour on the same page is the kind of thing that
   only ever shows up as a defect later. The 12px below the type and the 1rem
   between numeral and label are the reference's, unchanged.

   *** THERE IS NO min-height ON THIS HEAD ANY MORE, AND THE RULE THAT CARRIED
   IT WAS OUTLIVED BY ITS OWN COMMENT. *** A 768–991 block set `min-height: 64px`
   to put the hairlines of a row on one line when a label wrapped and its
   neighbour did not. Deleted 10-01. Measured at 768, the narrowest width in that
   band and therefore the worst case:

     all six labels   1 line, 17.7px   head would be 30.7px   forced to 64px

   Nothing wraps, so nothing needed levelling, and the floor was adding ~33px of
   empty air above the hairline on every one of the six heads at once.

   *** THE NUMBER AND THE LABEL'S FONT-SIZE WERE A PAIR, AND ONLY ONE OF THEM
   MOVED. *** 64px was the two-line head measured and rounded up WHEN THE LABEL
   WAS .95rem. The comment said in as many words that any change to
   .hv4-step-label's size had to re-measure it. The label went back to .85rem to
   match .fa-case-approach-label, and at that size the labels stopped wrapping
   altogether — so the floor did not become merely wrong by a few pixels, it
   became a rule with no job, still running.

   ⚠️ THIS IS THE SAME FAILURE THE DELETED COMMENT ALREADY DESCRIBED HAPPENING TO
   ITSELF. 09-21 found the min-height applying to a track where nothing wrapped,
   holding ~17px of empty air on four of six heads, and called that the "spacing
   all around feels awkard" complaint. The fix scoped it to the one band that
   still wrapped. Then the thing it was scoped to went away and the fix stayed.
   A rule derived from another rule's value needs a reason to be re-checked, and
   "the comment says to" is not one — nobody reads the comment on the rule they
   are not editing.

   If a label is ever long enough to wrap in a two-column band again, the right
   answer is the same shape as before: measure the wrapped head at the narrowest
   width in that band and declare the floor inside that band only. Six steps read
   across a row are a matched set, and a matched set is one fixed length used
   once, not six content heights (feedback_matched_pairs). Do not reach for
   `align-items: stretch` or a grid — the heads are in separate grid items.

   ⚠️ And re-measure rather than assume, because the container's clamp() gutters
   shrink faster than the viewport: two columns at 1024 are 421px, WIDER than
   three were at 1440. "Narrower viewport" does not mean "narrower column". */
.hv4-step-head {
    display: flex;
    align-items: baseline;
    gap: 1rem;
    padding-bottom: 12px;
    border-bottom: 1px solid rgba(6, 41, 53, .14)
}

/* The numeral. Generated, so no markup carries it — see the counter note above.
   `decimal-leading-zero` is what gives 01 rather than 1; the reference types
   "01" into the template by hand and this is the same glyph without the hand.

   *** THE FIXED BOX IS WHAT MAKES THE SIX LABELS START ON ONE LINE. LEAVE IT. ***
   09-25, at 375, where the three columns have collapsed to one and all six
   numerals stack: "this numbering feels off."

   leo has PROPORTIONAL figures, not tabular ones, so each numeral is a
   different width. Measured on canvas at 30px italic:

     01  28.44px      04  32.61px
     02  32.22px      05  31.20px
     03  31.74px      06  32.13px

   A 4.2px spread, and because the label follows the numeral in a flex row with
   a 1rem gap, that spread lands on the LABELS: they began at x = 66, 70, 70,
   71, 69, 70. Down a stacked column that is a visibly ragged left edge on six
   consecutive rows. Across three desktop columns it is nearly invisible, which
   is why it survived until the page was looked at on a phone.

   `font-variant-numeric: tabular-nums` is the proper typographic fix and it was
   tried FIRST — it does nothing here, because the font has no tabular figure
   set to switch to. Verified: label positions identical before and after. Do
   not re-try it.

   1.15em is the widest numeral (32.61 / 30 = 1.087em) plus a little. It needs
   `display: inline-block` because a width cannot apply to an inline box, and
   inline-block keeps the baseline the flex row aligns on, so the lockup does
   not move: head heights stayed 63/43 and every label's top was unchanged.
   Re-measure the em value if the font-size or the family changes.

   ⚠️ .fa-case-approach-num in styles.css is the same construction with the same
   defect, on five facade pages, and it does NOT have this fix. That asymmetry is
   deliberate and flagged rather than silently mirrored — see the consistency
   note on .hv4-step-label below, which is the standing rule for this lockup. */
.hv4-step-head::before {
    content: counter(hv4s, decimal-leading-zero);
    display: inline-block;
    min-width: 1.15em;
    font-family: "leo", serif;
    font-style: italic;
    font-weight: 500;
    font-size: 1.875rem;
    line-height: 1;
    letter-spacing: -0.01em;
    color: var(--dark-green)
}

/* *** COLOUR AND SIZE, 09-21: "the font colors seem boring. and the font seems
   a little small in the list." ***

   THE LABEL IS --dark-navy NOW, NOT --text-muted. --text-muted came straight
   from the .fa-case-approach reference, where it is right because that block's
   labels sit inside a dense case study and are not meant to lead. Here the six
   labels ARE the content — the bodies are one line each — so a muted label left
   the whole list reading at one flat weight. Navy is the page's strongest type
   colour (13.6:1) and it puts the labels above the bodies, which is the order
   they should be read in.

   *** THE SIZE IS BACK TO .85rem, 09-30, AND IT IS styles.css's NUMBER. ***
   "they are so big compared to other labels on the site. what do we do for the
   our approach section with those same labels here" — linking erp-integration,
   which is one of the five pages running .fa-case-approach. That rule is
   `font-size: .85rem` (styles.css:5002), so the question answers itself:
   liminalarc.co is the standard and this block is a copy of that component.

   THE 09-21 NOTE BELOW THIS ONE ARGUED ITSELF INTO THE WRONG NUMBER AND IT IS
   WORTH SEEING HOW. "the font seems a little small in the list" was read as a
   licence to go UP, and the justification — ".85rem was a tax paid for a 283px
   three-column track, and two columns removed the constraint" — is true and
   still does not licence it. Removing a constraint is not a reason to move; the
   reason to sit at .85rem was never the track, it was the other five pages.
   A borrowed component's size is not a free variable just because the borrower
   has more room.

   It was also the LARGEST small-caps label on this page, above .hv4-rg-label's
   14px, .hv4-graphic-head's 13px and the 12px chart floor — a step name outsized
   every axis, key and eyebrow around it. 13.6px puts it back between the ring
   label and the graphic head, which is where a thing of its rank belongs.

   THE COLOUR STAYS --dark-navy. That was the other half of 09-21 ("the font
   colors seem boring") and it is a separate decision from size, made for a
   reason that still holds: here the six labels ARE the content — the bodies are
   one line each — where on the facade pages they sit inside a dense case study
   and are not meant to lead. Size follows the site; weight does not have to.

   *** MEASURE THE FIT AT 992, NEVER AT 1440. *** At 1440 the head is 545px and
   nothing is close; at 992, the narrowest width that is still two columns, the
   head is 408px and the budget is 408 − 34 numeral − 16 gap = 358px. The old
   CREATE THE CONDITIONS INSIDE IT measured 358px at .95rem — zero slack, which
   is what made that value defensible at all. The label is now CREATE THE
   CONDITIONS at .85rem and there is real room. Re-measure at 992 after any
   change to the size, the .22em tracking, the label text, or the numeral/gap.

   `text-wrap: balance` IS FOR THE ORPHAN, 09-21: "no orphans". At three columns
   CREATE THE CONDITIONS INSIDE IT broke as "…INSIDE / IT" and left a two-letter
   line. Nothing wraps at two columns so balance currently has nothing to do —
   it stays because 768–991 still wraps, and because it is the right lever rather
   than an &nbsp; in the markup: the wrap point moves with the column.

   It works here only because this is a FLEX ITEM. `text-wrap` applies to block
   containers, and .hv4-step-label is a <span>; flex blockifies its children, so
   the span is a block box and balance takes. If .hv4-step-head ever stops being
   a flex row, this silently stops working.

   *** THERE IS NO OPTICAL SHIFT ON THIS LABEL, AND THAT IS A DECISION. BUILT
   09-22, REVERTED THE SAME DAY. DO NOT REBUILD IT HERE. ***

   09-22: "the number and text feels a bit off." The observation is correct and
   the measurement backs it. .hv4-step-head is `align-items: baseline`, taken
   unchanged from .fa-case-approach-head. The two glyph heights are nearly 2:1 —
   ink above the shared baseline is 21.3px for the numeral against 10.8px for
   the caps — so one baseline parks the label on the FLOOR of a numeral twice
   its height. Their centres sit 10.5px and 5.3px above the baseline, so the
   label wants `position: relative; top: -5px` to bring the two centres level.
   That was built, measured and verified, and it does look better in isolation.

   *** IT CAME OUT FOR CONSISTENCY, NOT BECAUSE IT WAS WRONG. *** This lockup is
   not ours alone: .fa-case-approach-head runs the identical construction on the
   five facade pages (ai-readiness, cloud-optimization, asset-modernization,
   erp-integration, enterprise-transformation). Fixing only the homepage made
   the homepage the odd one out of six, and the alternative — the same shift in
   styles.css, ~6px there because that label is .85rem — is a site-wide change
   nobody asked for. 09-22: "let's just keep it how the other pages have it so
   we don't have to do a universal fix. instead make the home page match."

   *** SO THIS IS NOW COUPLED TO styles.css. *** If .fa-case-approach-label ever
   gains an optical shift, this label takes the matching one, re-measured from
   .95rem and not copied from that rule's number. The two move together or not
   at all.

   Two things that look like the fix and are not, both built and measured before
   the revert, recorded so they are not retried:
   - `align-items: center` lands within 0.3px of the shift on a ONE-LINE label
     and breaks the moment one wraps. At 768–991 these DO wrap, and centring
     then floats the numeral to the middle of a two- or three-line block instead
     of leaving it beside line one. Any future fix keeps `baseline` and moves
     the label, because it is always line one the numeral pairs with.
   - Dropping the numeral instead holds the label on the hairline but pushes the
     numeral to 8px off a rule the label clears by 13. */
.hv4-step-label {
    font-family: "brandon-grotesque", sans-serif;
    font-weight: 700;
    font-size: .85rem;
    letter-spacing: .22em;
    line-height: 1.3;
    text-transform: uppercase;
    text-wrap: balance;
    color: var(--dark-navy)
}

/* 14px under the head, which is .fa-case-approach-item's flex gap written as a
   margin because this list is a grid of blocks rather than a flex column.

   *** 1rem / --dark-teal, 09-21, same note as the label: "the font colors seem
   boring. and the font seems a little small in the list." *** It was .9375rem
   --text-muted, which is the page's small-body pair and was sized for a 283px
   three-column track. At two columns the bodies have 545px and were reading
   small and grey against a lot of space.

   --dark-teal, NOT --dark-navy: navy is now the label, and a body in the same
   colour as its own heading flattens the pair the label change just created.
   Teal is 9.28:1 and is the page's existing second-darkest type colour, so this
   adds no new value.

   *** THE STEP BODIES ARE NOW DARKER THAN THE .hv4-lead ABOVE THEM (17.6px
   --text-muted), AND THAT IS DELIBERATE. *** The lead is an intro — bigger and
   lighter is the normal treatment for one. The steps are the thing the section
   is actually for. If that ever reads as inverted, the fix is the lead, not
   these. */
.hv4-steps li p {
    font-family: "leo", serif;
    font-size: 1rem;
    line-height: 1.6;
    color: var(--dark-teal);
    margin: 14px 0 0;
    text-wrap: pretty
}

/* ── The slice figure, "three beats" ──────────────────────────────────────────
   Built to the 09-18 handoff ("Slice visualization options", option 4b), which
   declares itself HIGH FIDELITY: colours, sizes, spacing and type are final and
   are matched exactly. That is why this block is full of raw pixel values —
   26px, 24px, 20px, 8px, 12px, 10px, 32px, 64px, 6px — instead of the page's
   usual rems off the type scale. DO NOT TIDY THEM INTO REMS. Three of them are
   coupled to each other (see the arithmetic below) and rounding any one to the
   nearest .25rem breaks the fit inside the 420px box.

   THE LAYOUT IS FIVE TRACKS: beat, arrow, beat, arrow, beat, at
   `1fr 20px 1fr 20px 1fr` with an 8px gap in a 420px box. So a beat is
   (420 − 40 − 32) / 3 = 116px wide, and that 116 is the number every horizontal
   measurement below has to live inside:

       column 1   left: 20%          -> 23.2 .. 47.2
       column 2   left: 20% + 32px   -> 55.2 .. 79.2      (32 = 24 wide + 8 gap)
       column 3   left: 20% + 64px   -> 87.2 .. 111.2     (4.8px of slack)

   Widen the figure and the 20% moves while the 32/64 do not, so the three
   columns drift apart; narrow it and column three runs out of the beat. THE
   420px IS LOAD-BEARING. If it ever has to change, convert the offsets to
   percentages in the same edit.

   THE COLUMNS OVERSHOOT THE STACK BY 6px TOP AND BOTTOM, by `top: -6px;
   bottom: -6px`. That is the handoff's own instruction and it is the figure's
   one real idea: a column that stops flush with the layers reads as three boxes
   that happen to be filled, and one that runs past them reads as a cut taken
   through the whole thing. Do not "fix" the overhang.

   THE LAYER HAIRLINES ARE SHARED, NOT DOUBLED. Every layer drops its own
   bottom border and only the last one puts it back, so the line between two
   layers is 1px rather than 2px. Three boxes, four lines.

   WHY IT REPLACED VERSION TWO. Version two was three labelled bands with one
   green cut and an italic caption. It drew "one slice" and nothing else — the
   headline's other two clauses, ninety days and then again, had no picture. The
   three beats carry the whole sentence: spread thin, one cut, the cut repeating
   with a dashed one waiting. Version two's markup and its .hv4-slice-stack /
   -band / -cut rules are DELETED, not commented, and live in git at 0ac70450.

   *** CONTRAST, 1.4.11 asks 3:1 for a graphical object, and this figure has two
   shortfalls. Both are the handoff's own values, and both are measured against
   white — which is what sits behind the figure, but is the BEST CASE for this
   section rather than the whole story: #hv4-delivery is a mountain photograph
   under a 20% white veil, and the ground darkens further down as the silhouette
   rises. The figure sits at the top where the veil is effectively paper. Move
   it down the section and re-measure before trusting these:

     --light-green #4abd92 columns   2.34:1   SHORT
     hairline rgba(6,41,53,.28)      ~1.75:1  SHORT

   The green is the one that matters — the columns ARE the information, and this
   is the same #4abd92-on-white figure already flagged on the order bars and on
   version two's cut. The routes are the same as there: a 1px --dark-green
   outline on the columns (3.87:1) without touching the fill, or taking the
   hairline from .28 to about .45. Neither is built, because the handoff is
   marked match-exactly and because the labels beneath each beat carry the same
   meaning in type at 5.70:1 and 15.3:1. DO NOT DARKEN THE FILL. ***

   Also flagged: the labels are 10px. That is below the site's smallest type
   anywhere else and it is the handoff's figure, not a derived one. */
.hv4-slice-fig {
    --beat-layer-h: 26px;
    --beat-col-w: 24px;
    --beat-step: 32px;
    --beat-hairline: rgba(6, 41, 53, .28);
    width: 420px;
    margin: 0;
    padding-top: 8px;
    display: grid;
    grid-template-columns: 1fr 20px 1fr 20px 1fr;
    column-gap: 8px;
    align-items: center
}

/* `position: relative` here and nowhere else — the columns are positioned
   against the stack of layers, not against the beat, so they cannot be pushed
   out of register by the label's margin. */
.hv4-beat-layers {
    display: grid;
    position: relative
}

.hv4-beat-layer {
    height: var(--beat-layer-h);
    border: 1px solid var(--beat-hairline);
    border-bottom: none
}

.hv4-beat-layer:last-child {
    border-bottom: 1px solid var(--beat-hairline)
}

/* Beat one: the horizontal rollout. One thin fill across the TOP layer only,
   at .12 against the hairline's .28, so it reads as something spread rather
   than something built. The other two layers stay empty on purpose — that
   emptiness is the "nothing finished". */
.hv4-beat-layer--spread {
    background: rgba(6, 41, 53, .12)
}

.hv4-beat-col {
    position: absolute;
    top: -6px;
    bottom: -6px;
    width: var(--beat-col-w);
    background: var(--light-green)
}

/* THE COLUMNS MIX A PERCENTAGE WITH FIXED OFFSETS, and that is the one fragile
   thing in this figure. `left: 20%` moves with the beat; the step between
   columns does not. A beat has to be at least 20% + 2 steps + 24px wide or the
   dashed column runs out of its own stack — at a 32px step that is a 112px beat,
   and the beat is (figure width − 40 for the two arrows − 32 for the four
   gaps)/3. 420px gives 116px, which clears it by 4. Anything narrower needs the
   step brought down with it, which is why
   it is a variable and not the handoff's literal 32/64. */
.hv4-beat-col--1 {
    left: 20%
}

.hv4-beat-col--2 {
    left: calc(20% + var(--beat-step))
}

.hv4-beat-col--3 {
    left: calc(20% + 2 * var(--beat-step))
}

/* The dashed one is the word "again". It has to come after --3 in source order,
   because --3 sets only `left` and this sets the fill; `box-sizing: border-box`
   is what keeps it the same 24px as its two solid neighbours rather than 26px. */
.hv4-beat-col--next {
    background: transparent;
    border: 1px dashed var(--light-green);
    box-sizing: border-box
}

.hv4-beat-label {
    margin-top: 12px;
    font-family: "brandon-grotesque", sans-serif;
    font-weight: 700;
    font-size: 10px;
    line-height: 1.5;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--dark-navy)
}

/* Beat one's label is muted and the other two are navy, which is the figure
   saying which beats it is for. The rollout is the thing being argued against,
   so it is the only one not printed in full ink. */
.hv4-beat-label--muted {
    color: var(--text-muted)
}

/* `padding-bottom: 28px` is what puts the arrow level with the LAYER STACKS
   instead of level with the beat as a whole. The beat is stack (78px) + label
   margin (12px) + label, and `align-items: center` would otherwise centre the
   arrow against all of that and drop it below the boxes. The 28 is roughly half
   the label block plus its margin — if the label ever goes to two lines at this
   width, this number moves with it. */
.hv4-slice-arrow {
    font-family: "leo", serif;
    font-size: 16px;
    line-height: 1;
    color: var(--text-muted);
    text-align: center;
    padding-bottom: 28px
}

/* At 1200-1439 the gutters drop back to 48px — the wide gutters are a 1440
   luxury. The figure's own track no longer has to give anything up for this:
   it is opposite the headline now, not opposite the steps, so narrowing the
   container takes width out of the intro rather than out of the list. */
@media(max-width: 1199px) {
    .hv4-delivery-head {
        gap: 3rem
    }

    /* The track count no longer changes here — it is two columns from 992 up
       since 09-21, so this is a gap-only rule now: 4rem between two columns is
       a 1200-plus luxury for the same reason the container gutters are.

       This block used to drop three columns to two, on the measurement that
       three at 1024 put the step bodies at 271px, about 41 characters of 15px
       serif, under the ~45 a line of prose needs. That measurement is why the
       desktop layout is not three columns anywhere now. */
    .hv4-steps {
        column-gap: 3rem
    }
}

/* Below 991 the head row stacks and the figure sits between the lead and the
   steps — above the steps, which is where it was asked to sit the last time
   this section stacked, and where the handoff puts it too ("below ~900px stack
   the graphic under the intro paragraph, left-aligned"). 991 is the page's own
   breakpoint and the nearest one to the handoff's 900.

   The handoff's `margin-top: 40px` is not written: the stacked row's 2.5rem gap
   is already 40px. Two of them would be 80.

   `padding-top` goes to 0. Its 8px was an optical kern against the headline's
   cap height, and stacked there is no cap height beside it to kern to.

   Width goes to 100% with the 420 kept as a cap, which is the handoff's own
   allowance. It stays 420 down to 468px of container and shrinks below that.

   THE STACK RULE HAS TO CARRY :has() TOO, and this is not decoration. `:has()`
   takes the specificity of its argument, so `.hv4-delivery-head:has(.hv4-slice-fig)`
   is two classes and a bare `.hv4-delivery-head` here loses to it no matter how
   far down the file it sits. Written without it the row stayed two columns at
   375 — a 0px track and a 420px one — and the figure hung 154px off the right
   of a 330px container. Do not simplify this selector. */
@media(max-width: 991px) {
    .hv4-delivery-head,
    .hv4-delivery-head:has(.hv4-slice-fig) {
        grid-template-columns: minmax(0, 1fr);
        gap: 2.5rem
    }

    .hv4-slice-fig {
        width: 100%;
        max-width: 420px;
        padding-top: 0
    }
}

/* One column below 767 — the same 2/1 tail .hv4-conditions and .transform-cards
   use. The handoff collapses the steps at "roughly 640"; 767 is the page's own
   breakpoint and the nearest one to it.

   THE STEP COMES DOWN WITH THE FIGURE. At 375 the container is 330px, so a beat
   is (330 − 40 − 32)/3 = 86px, and the dashed column at 20% + 64 + 24 would end
   at 81.2px — inside the stack, but only just, and the 8px gaps between the
   three columns are gone. 24/18 needs .2×86 + 48 + 18 = 83.2px, keeps 6px
   between columns, and leaves the drawing reading as three separated cuts
   rather than one 72px block. Below about 300px of container it runs out again;
   the page has no ground below 320, and the labels wrap before the drawing
   breaks. */
@media(max-width: 767px) {
    .hv4-slice-fig {
        --beat-col-w: 18px;
        --beat-step: 24px;
        max-width: none
    }

    .hv4-steps {
        grid-template-columns: minmax(0, 1fr);
        gap: 1.75rem
    }
}

/* ==========================================================================
   9. Why LiminalArc — navy band carrying the about-us mesh. Headline, the
      argument in two columns split by a hairline, and a pull quote breaking
      the left column.

      GROUND: blue topo → light → #259271 green → navy, all within two days.
      Navy is where it rests, and it is also the only one of the four on which
      the section's two columns of 17px body copy actually clear AA.

      ARTWORK: the mountain is gone from here, 09-17 — "see how the bkg is like
      this on the about us page. Can we use that same background layout for the
      we built these conditions section instead of the mountain image", and
      "let's move that mountain into the how to get production to match part".
      Both halves of that are real: the mesh arrives here, and the mountain is
      not deleted, it moves to #hv4-correction (section 7).

      The swap also removes a problem the navy ground had just created. The
      mountain is dark linework on a white ground with no alpha, so it was
      composited with `mix-blend-mode: multiply` — which is what dropped the
      white away. Multiply against navy is very nearly a no-op: the ground is
      already near-black, so there is nothing left to darken and the artwork
      would have all but vanished. mesh-v3.png has a real alpha channel and
      needs no blend mode at all, which is why it is the right artwork for a
      dark ground and the mountain is the right artwork for a light one.
   ========================================================================== */

/* isolation + overflow are for the ::after artwork's stacking context and its
   clipping. They used to come from .hv4-section--blue, which owned the topo;
   neither the --green modifier nor --navy carries them, so they live here. */
#hv4-why {
    isolation: isolate;
    overflow: hidden
}

/* THE EYEBROW. `.fa-hero-eyebrow` from the facade heroes, minus its icon badge:
   a 32px rule, a 1rem gap, a tracked cap. Copied rather than approximated, per
   the standing rule about borrowing a reference component.

   The badge is dropped because it is a per-practice icon and there is no icon
   for "Why LiminalArc".

   THE 32px RULE IS GONE TOO, 09-17: "I don't think the eyebrow needs the dash".
   It came across with the reference component, where it anchors a 12px cap
   floating above a 64px hero h1. Here the cap sits 20px above a 43px h2 on a
   dark ground with nothing else in the corner, so the tracked caps hold the
   position on their own. .hv4-eyebrow-rule is deleted from the markup and from
   this file; a two-element flex row with one element left is now just the label,
   so the flex and its gap go with it. Do not reinstate the dash — the same green
   dash was already removed once from the order cards ("remove the little green
   dashes too"), which makes this the second time this ornament has been
   rejected on this page.

   YELLOW, 09-17: "and then make the eyebrow yellow". --bright-yellow #f5b939 at
   8.6:1 on navy, which is the reference component's own value — so this is the
   facade hero's eyebrow restored rather than a new colour. It shipped white for
   one day only because the section was briefly #259271, where yellow measured
   about 2.1:1 and would have been the faintest thing in the band; navy removed
   that constraint. It also ties the eyebrow to the page's other two gold
   italics, in the hero h1 and in the closing CTA.

   1.25rem under it, not the reference's 1.75rem: the reference sits above a
   64px h1 in a hero, this above a 43px h2 in a band. */
.hv4-eyebrow {
    display: block;
    margin-bottom: 1.25rem
}

.hv4-eyebrow-label {
    font-family: "brandon-grotesque", sans-serif;
    font-size: .78rem;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    line-height: 1
}

/* --bright-green, 09-17: "let's make why liminalarc our bright green." Was
   --bright-yellow. #6ce0b5 measures 9.47:1 on --dark-navy and 5.73:1 on
   --dark-teal, against yellow's 9.03:1 and 5.46:1 — so this is not a contrast
   trade, it is the same standing on both grounds.

   The yellow rule on the #hv4-why pull quote came off in the same breath ("ditch
   the yellow line"). That rule existed to pick up THIS eyebrow directly above it;
   once the eyebrow stopped being yellow the rule was the only yellow left in the
   band, pointing at nothing. Do not restore one without the other. */
.hv4-section--navy .hv4-eyebrow,
.hv4-section--teal .hv4-eyebrow {
    color: var(--bright-green)
}

/* THE MESH, taken from the about-us page: "see how the bkg is like this on the
   about us page. Can we use that same background layout for the we built these
   conditions section instead of the mountain image."

   Same file as the reference (.name-meaning-container-wrapper in
   assets/css/website-v2/about-us.css:66), which draws it at `cover` with no
   blend mode and no opacity. It shipped that way here for a day and then took
   three changes on 09-17, all in one instruction: "the graphic needs the
   luminosity filt but it also feels too low I would like it to fill more of the
   frame."

   LUMINOSITY. mix-blend-mode: luminosity keeps the mesh's light and dark and
   takes its hue and saturation from the navy behind it. The source is cyan and
   electric blue on transparent — two hues the page does not use — so at full
   strength it was the only thing in the section not speaking the brand's
   palette. Blending pulls every line onto the navy's own hue and leaves the
   drawing intact, which is why this is a blend mode and not an opacity drop:
   fading it would have cost the fine lines before it cost the colour.

   Not `multiply`, which is what the mountain uses. The two artworks need
   opposite treatments and it is worth keeping straight why: the mountain is a
   .webp with NO alpha — dark linework on a white ground — so it needs multiply
   to drop the white away, and multiply on navy is near a no-op, which is why it
   lives on the light section. mesh-v3.png is 2200x2282 WITH a real alpha
   channel, so nothing needs dropping and the only problem was hue.

   TOO LOW, AND FILLING MORE OF THE FRAME — the same fix twice. The artwork
   occupies roughly the top 78% of its own canvas, with the vortex eye at about
   42% down, and at `center top/cover` the image renders 1440x1493 against a
   ~730px section: only the top half is on screen, which put the eye at about
   630 of 730 and left the bottom quarter of the frame empty.

     position   42% 52% above 992, `center 52%` below it. A percentage position
                SPLITS THE OVERFLOW, and both axes work that way here because at
                150% the image is bigger than the box in both directions.

                Vertically: 52% of the overflow hidden above the box, 48% below,
                so RAISING the number moves the artwork UP the section. It went
                36% → 52% on 09-17 ("still need to move texture up a bit too",
                then "the arc of the texture should start around where the
                screenshot is", pointing at the top of the left column), which
                puts the vortex eye near the top of the copy rather than level
                with it. One value at both sizes.

                Horizontally the sign is the one people get wrong: LOWERING the
                number moves the artwork RIGHT, because 0% pins the image's left
                edge to the box's left edge and lets the rest hang off to the
                right. 50% → 35% → 42% → 46%, all on 09-17. "Can we move the
                texture --> this way a little so it's more in the middle margin,"
                then "too far over do an amount in between the two," then "the
                texture needs to move this way <-".

                At 1440 the overflow is 2160 - 1440 = 720px, so each point is
                7.2px. Read as moves from the original 50%: 35% was 108px right
                and landed the arc's crest in the right column; 42% was 58px
                right, onto the 88px gutter, which is what "middle margin" meant;
                46% is 29px right, so the crest now sits on the gutter's LEFT
                half rather than its centre.

                46 is a HALVING of the 42 move, not a step past it. The two
                corrections bracket the answer from opposite sides — 35 was too
                far right, and 50 was the state being corrected in the first
                place — so the remaining range is 42-50 and this splits it. If
                this is still too far right, the next value is 48, not 50: going
                back to 50 discards the original instruction.

                Only above 992, because below it there are no columns and so no
                middle margin to sit in.

     opacity    .55, 09-17: "the texture seems a bit too bright." This is a
                second lever on top of the blend mode, not a replacement for it —
                luminosity fixes the HUE and opacity fixes the STRENGTH, and the
                note below about fading costing the fine lines is the reason it
                is .55 and not lower. The mesh also now runs up through the wave
                band (see the mask on .wave-top), which put more of it on screen
                and is half of why it read as too strong.
     size       150% auto above 992, up from 122% on 09-17: "can we scale the
                texture to be a bit bigger." Width 150% of the section, height
                150 x (2282/2200) = 155.6% of it. At 1440 that is 2160x2240
                against a ~770px band, so it covers several times over and the
                only thing the number is doing now is setting the SCALE of the
                arc, not whether the box fills.

                Enlarging interacts with `position`: the taller the image, the
                more overflow there is for the 52% to split, so the artwork
                effectively drifts as the scale changes. 52% was verified to
                still put the arc at the top of the left column after this
                change. If the size moves again, re-check the position — these
                two are not independent.

                *** 150% IS SETTLED. DO NOT SCALE IT UP AGAIN. ***

                It was taken to 230% on 09-17 for "this needs to wrap" — a
                screenshot of the seam where the mesh appeared to stop about two
                thirds across, leaving the right quarter flat teal — and reversed
                the same hour: "it was looking good now it's huge. roll back."
                185% was tried on the way and also rejected.

                Two things were learned there and both are worth keeping:

                  - THE BOX WAS ALWAYS COVERED. At 150% the image renders 2160
                    wide in a 1440 box, so an apparently empty right edge is
                    never a `cover` failure. `repeat-x` cannot fix it either, and
                    was tried: a 2160 tile in a 1440 box puts the next copy at
                    x=1829, off screen. Dropping to 100% to make it tile made the
                    mesh small and faint and left the right emptier still.
                  - What actually thins on the right is THE ARTWORK'S OWN EDGE —
                    the spiral is inset inside mesh-v3.png's black canvas. The
                    only lever that moves it is scale, and scale is exactly what
                    cannot be spent: past ~150% the spiral stops reading as an
                    arc with a sweep and becomes an even fine grid. That is the
                    "huge" the user reversed.

                So coverage of the far right is a cost this section accepts.

                THE POSITION IS NO LONGER 46%, 09-18 — "the graphic angle needs
                to go this way -->", which is the user reversing their own "the
                texture needs to move this way <-" from the day before. It is now
                90%. Scale is still not the lever and 150% still stands.

   `cover` stays as the base, for phones. There the section is far taller than
   it is wide, `cover` scales by height, and the artwork becomes abstract
   texture — which is correct at that size and is what a fixed 150% would break.

   inset: 0 — a full-section box, which is the simple case the mountain never
   was. Nothing has to be aligned to a landmark inside the image, so there is no
   width-driven height and no clip geometry to keep in step.

   2.1MB, which is large, but it is the same request the about-us page already
   makes, so any visitor who has seen that page has it cached. */
#hv4-why::after {
    content: "";
    position: absolute;
    inset: 0;
    background: url("../images/rebrand-images/About-Us/mesh-v3.png") center 52%/cover no-repeat;
    mix-blend-mode: luminosity;
    /* *** .55 STANDS ABOVE 767. THE 09-30 STRENGTHENING IS A PHONE FIX AND ONLY
       A PHONE FIX. *** It was applied here, to every breakpoint, and reversed
       the same day: "I don't like the changes you made to the why liminalarc
       text on desktop. the change I wanted was just for mobile. roll back for
       desktop/tablet." The ≤767 override at the bottom of this section is where
       .85 lives now.

       .55 is itself a correction — 09-17, "the texture seems a bit too bright"
       — so desktop has been asked about twice, in opposite directions, and the
       09-17 answer is the one that survived. DO NOT RAISE THIS AGAIN WITHOUT
       BEING ASKED ABOUT DESKTOP SPECIFICALLY. A complaint that the mesh is
       faint is not automatically a complaint about this value at every width;
       ask which screen it was seen on first.

       WHY OPACITY IS THE ONLY LEVER THAT WORKS HERE, measured before touching
       anything, and still true for the phone rule below. mesh-v3.png is
       2200x2282 and 98.8% FULLY TRANSPARENT — only 1.2% of its pixels carry any
       ink at all, and where they do they are bright (luminance 191 against the
       band's 65). It is a sparse hairline drawing, not a texture, so the layer
       was never dense and no amount of scaling adds ink. */
    opacity: .55;
    pointer-events: none;
    z-index: -1;
    transform: scaleX(-1)
    /* *** THE LAYER IS MIRRORED. THE HISTORY LOOKS LIKE TWO REJECTIONS AND IT IS
       ONE — READ THIS BEFORE "RESTORING" ANYTHING. ***

       The 09-18 exchange reads as three requests and two rejections, but one of
       those rejections was mine to misread, not the user's to make:
         1. "can we see what it looks like if we flip the texture horizontally"
         2. "I don't like the flipped texture"           <- a real rejection
         3. "let's try flipping horizontally the texture again"
         4. "let's put this image back in its natural position"
         5. "I wanted to try flipping the texture horizontally again"

       (4) was about a PHOTOGRAPH, not this layer — it arrived attached to a crop
       of a photo and meant that photo's framing. It was read as a fourth
       instruction about the mesh and the flip was removed. (5) is the
       correction. The standing score is ONE rejection and TWO requests, the most
       recent of which is live. DO NOT COUNT (4) AS A VOTE.

       WHAT THE FLIP COSTS, which is why (2) happened and is the thing to watch.
       The spiral is inset inside mesh-v3.png's canvas, so its thin, sparse outer
       edge is a real feature of the artwork and always sits on one side.
       Unflipped it falls on the RIGHT, where this section already accepts a
       lighter far edge (see the 150% note above) and where there is no copy.
       Mirrored it lands on the LEFT — behind the headline and the lead — and the
       dense part of the arc moves off the empty gutter and onto the words. That
       trade is accepted. It is the first thing to reverse if the section ever
       reads as too busy behind the headline.

       THE FLIP IS NOT A FIX FOR THE HEADLINE COLLISION and must not be offered
       as one — the collision is a POSITION problem and this is a MIRROR. It was
       reached for straight after "can we move or rotate the texture slightly so
       it doesn't cross on the w". That was answered with position on 09-18 and
       the mirror was not touched; see the note on the ≥992 override below.

       AND UN-MIRRORING WOULD MAKE THE COLLISION WORSE, NOT BETTER, which is the
       opposite of what this comment used to imply. Measured over the h2's box at
       52% and 150%, stepping the position across its whole range: mirrored runs
       33% coverage down to 8.7%, unmirrored runs 36.8% down to 22.2%. Mirrored
       beats unmirrored at EVERY position. There is no version of "take the flip
       off to clear the headline" that works. Do not offer it.

       *** MIRRORING SILENTLY INVERTS `background-position`. *** 46% means 29px
       RIGHT of centre; mirrored, the same 46% renders 29px LEFT of it — so a
       HIGHER number moves the artwork RIGHT on screen, which is backwards from
       everywhere else on this page. The ≥992 override below is written in
       FLIPPED coordinates for that reason. If the flip is ever removed, that
       value has to be re-derived, not left alone. */
}

/* 46% -> 90%, 09-18. Two messages, and they turn out to be one fix:
   "the graphic angle needs to go this way -->" and "you can see the texture
   crashing into the headline".

   *** THIS IS WRITTEN IN MIRRORED COORDINATES. HIGHER MOVES THE ARTWORK RIGHT
   ON SCREEN. *** Unmirrored it would be the other way round. That is the whole
   reason the arrow and the collision resolve together rather than fighting:
   going right takes the dense middle of the spiral off the headline, which is
   in the left 58% of the section.

   Sampled over the h2's box (142.6, 143, 835x100 at 1440), stepping the value
   right at a fixed 52% and 150%:

     46%  head 24.7   left col 26.1   right col 35.9   section 29.8   <- was
     70%  head 18.6   left col 27.8   right col 36.1   section 29.0
     90%  head 11.6   left col 29.2   right col 32.8   section 26.7   <- is
    100%  head  8.5   left col 28.5   right col 30.4   section 25.0

   90 and not 100 because 100 is the end of the travel — the artwork's edge
   flush to the section's — and there is nowhere to go from there if this needs
   nudging again. 90 leaves a tenth of the range in hand and still more than
   halves the collision.

   THE TWO BODY COLUMNS COME OUT EVENER, and that was not the goal. At 46 the
   right column sat under 36% mesh against the left's 26 — a ten-point split
   across a 88px gutter, which is part of why the section read as lopsided. At
   90 it is 33 against 29. Do not undo this to chase the far right edge.

   Section fill drops 29.8 -> 26.7, three points. Watch this if the value is
   pushed further: the mesh has to keep filling the band or the navy goes flat,
   and that is the failure mode at the other end, not this one. */
/* *** 260%/70% WAS BUILT HERE 09-30 AND REVERSED THE SAME DAY. 150%/90% IS THE
   LIVE PAIR AND THIS IS NOW THE SECOND TIME SCALING DESKTOP HAS BEEN REJECTED.
   ***

   "I don't like the changes you made to the why liminalarc text on desktop. the
   change I wanted was just for mobile. roll back for desktop/tablet." The
   09-30 request — "the bkg on here is hard to see I think we need to scale it
   up and increase opacity" — was about the PHONE. Only the ≤767 opacity
   survives; see the base rule.

   The first rejection was 09-17 at 230%: "it was looking good now it's huge.
   roll back." 185% was tried on the way and also rejected. So the pattern is
   settled and it is not about the ink figures, which is what the 09-30
   measurements kept arguing:

     150% / 90%   band 1.94   head 2.92   <- live, and twice now the answer
     260% / 70%   band 1.73   head 1.34   <- measured better. Still rejected.
     260% / 50%   band 3.08   head 8.21   <- never. 09-18's reverted layout.

   260% renders the artwork at 1.7x native on a 1440 band, so the lines really
   do thicken and the headline collision really does halve. IT IS STILL WRONG.
   Past ~150% the spiral stops reading as an arc with a sweep and becomes an
   even fine grid — the "huge" of 09-17 — and no coverage number captures that.
   **On this layer, line weight measured is not line weight seen. Do not
   re-derive your way back into 260%.**

   *** THE 09-18 COMMENT ABOVE IS THE LIVE REASONING AGAIN — both numbers are
   its, unchanged, including the mirrored-coordinate warning. *** It was left in
   place rather than rewritten during the 09-30 attempt, which is the only
   reason this reversal was a two-line edit. Do the same to this one. */
@media(min-width: 992px) {
    #hv4-why::after {
        background-position: 90% 52%;
        background-size: 150% auto
    }
}

/* The phone treatment, and it is the one width where this layer is allowed to
   be scaled. 09-30, twice: "the bkg on here is hard to see I think we need to
   scale it up and increase opacity", then after the opacity-only answer, "the
   texture here is hard to see and small. can we scale it for mobile so it takes
   up the whole section?"

   *** OPACITY IS .62, DOWN FROM THE .85 BUILT EARLIER THE SAME DAY. 09-30:
   "this texture still isn't right it's hard to read the text." ***

   The two 09-30 notes pull in opposite directions — "hard to see" then "hard to
   read the text" — so it is worth writing down exactly what this layer does to
   a glyph, because the numbers are smaller than they look and the answer is not
   where you would expect.

   This is NOT a normal alpha overlay. Three multipliers stack:

     ink colour in mesh-v3.png       rgb(117, 181, 225), a LIGHT BLUE
     ink alpha, maximum              31/255 = 12.2%   (mean 18/255)
     mix-blend-mode                  luminosity

   Luminosity keeps the backdrop's hue and takes the source's lightness, and the
   ink is much lighter than the ground: Lum(ink) 167 against Lum(#0e4d64) 61. So
   every line LIFTS the teal toward white — the same direction the type is
   travelling. Composited, a line moves the ground by:

     opacity .55   ->  rgb(21, 84, 107)   text contrast 5.6:1
     opacity .85   ->  rgb(25, 88, 111)   text contrast 5.4:1
     opacity .62   ->  rgb(22, 85, 108)   text contrast 5.5:1

   *** SO CONTRAST IS NOT THE FAILURE AND CHASING IT WOULD HAVE MISSED THIS. ***
   Every one of those passes 4.5:1 against the copy's rgba(255,255,255,.78).
   What breaks is the GLYPH OUTLINE: a light hairline crossing a 16px serif at
   an angle reads as part of the letterform, and no ratio measures that. The
   line's absolute strength is what matters, and .85 raised it from 7 RGB units
   to 11 — 57% more line, for 0.2 of a contrast point.

   THE OTHER HALF OF THE PROBLEM IS THE SIZE CHANGE, NOT THE OPACITY. `cover`
   left the top third of the band nearly empty (see below), so most of the copy
   had no ink behind it at all. `auto 150%` fixed that and, in fixing it, put
   dense grid through the paragraphs for the first time. .85 was chosen against
   the old sparse crop and never re-judged against the new one.

   .62 keeps the layer meaningfully above the desktop .55 — the "it was faint"
   finding still stands — while giving back most of the line weight the copy
   was fighting. IF IT GOES FAINT AGAIN, THE ANSWER IS NOT OPACITY: raise the
   copy off rgba(255,255,255,.78), which buys 2 full contrast points and costs
   the texture nothing.

   *** SIZE. THIS NOTE USED TO SAY `cover` WAS ALREADY RIGHT HERE AND IT WAS
   WRONG ON BOTH HALVES OF THE CLAIM. *** It said the section is taller than it
   is wide so `cover` scales by height and the artwork "reads as abstract
   texture". Measured at 375: the section is 375x1347, the file is 2200x2282, so
   `cover` renders it 1299x1347 — scaled by height, correct, but that means

     - the window onto the drawing is 375 of 1299, i.e. 29% of its width. At
       that crop the arc has no curvature left in frame; it reads as a fine even
       grid, which is the "small" in the complaint.
     - vertical overflow is ZERO. `cover` fits the height exactly, so the `52%`
       vertical term in the base rule has nothing to split and is inert at this
       width. Whatever the middle 29% of the artwork happens to contain is what
       the section gets, and at the top of that strip it contains almost
       nothing — the top third of the band was bare navy. That is the "doesn't
       take up the whole section" half, and it is a framing accident rather than
       a coverage failure.

   `auto 150%` renders 1949x2021 into the 1347px box: 674px of real vertical
   overflow for the 52% to split, so the drawing runs edge to edge, and every
   cell is 1.5x its old size. Verified by eye at 375, which is under the 800px
   screenshot cap and therefore the one width where looking is allowed.

   *** 200% WAS BUILT AND REJECTED IN THE SAME PASS, AND IT IS THE SAME FAILURE
   09-17 FOUND ON DESKTOP. *** Thicker lines, more of them, and the sweep gone —
   the spiral straightens into a skewed grid and the band goes busy behind the
   headline while the bottom empties out again. So 150% is not a coincidence:
   it is the same ceiling the desktop rule found, reached from a completely
   different crop. Past it this artwork stops being an arc at any viewport.

   Height-relative, not width-relative, and that is deliberate. `150% auto` here
   would be 562x583 in a 1347px box and would not cover at all. The desktop rule
   one block up is width-relative for the opposite reason. The two are not
   transposable — check which axis is short before copying a number between
   them. */
@media(max-width: 767px) {
    #hv4-why::after {
        background-size: auto 150%;
        opacity: .62;
        /* *** THE ARC IS RAISED ON PHONES, 10-01: "can you make it so the arch
           of the graphic is higher up on mobile." PHONES ONLY — the base rule's
           52% still governs tablet and desktop, where the crop is completely
           different and the framing has been settled twice. ***

           THE DRAWING IS A THIN BAND INSIDE A MOSTLY EMPTY FILE, AND THAT IS
           THE WHOLE REASON A POSITION CHANGE DOES ANYTHING HERE. mesh-v3.png is
           2200x2282 but every pixel of ink lives between 26.5% and 51.9% of its
           HEIGHT, peaking at 30.1% — the rest of the canvas is transparent. So
           the artwork is a 579px strip floating in a 2282px frame, and moving
           the frame moves the strip a long way for a small percentage.

           Measured at 375, where the section is 1283px. `auto 150%` renders the
           file 1855x1924 into a 1283px box, leaving 641px of vertical overflow
           for the position to split:

                                   52% (was)   68% (now)
             top of the drawing       176         74
             densest row              246        143
             foot of the drawing      665        563

           Against the section's own landmarks — eyebrow 67, h2 111-172, opening
           paragraph 192-437, the .hv4-pullquote photo 469-625 — 52% started the
           arc level with the first line of the PARAGRAPH, so the headline and
           the eyebrow sat on bare teal and the sweep was already curving away
           by the time the eye reached it. At 68% the arc opens at the eyebrow,
           its densest pass runs behind the headline, and its foot is swallowed
           by the opaque photograph.

           THE MASK BELOW IS UNCHANGED AND IS WHY THIS IS SAFE. It is keyed to
           the COPY (34% = the foot of the paragraph, 55% = clear of the photo),
           not to the artwork, so sliding the artwork inside it costs nothing —
           the drawing now ends at 563, which is behind the photograph, so the
           part that loses strength is the part nobody can see.

           68% AND NOT MORE. The next stop up pushes the top of the drawing past
           the section's own top edge and the arc gets cut by the wave instead
           of opening under it. Note also that the densest row is now behind the
           H2 rather than behind 16px body copy — that direction is deliberate,
           because the known failure mode for this layer is a hairline crossing
           a small serif and reading as part of the letterform. A 36px headline
           is the robust place to put the ink.

           Vertical only. `transform: scaleX(-1)` on the base rule mirrors the X
           axis, so the horizontal term runs backwards and is left alone; Y is
           unaffected by the mirror. Two-value form, never three — see the
           background-position trap on #hv4-cost::before. */
        background-position: center 68%;
        /* *** THE MESH IS A TOP-OF-SECTION LAYER ON PHONES, 10-01. *** "let's
           put the bkg just as the top here." PHONES ONLY — asked and answered
           before writing anything, because the last note about this artwork
           ("the bkg on here is hard to see") was also a phone screenshot and I
           changed it at every width. Desktop and tablet keep the full-height
           layer; there is no mask outside this block.

           Measured at 375 the section is 1283px and the content lands at:
           eyebrow 67, h2 111–172, opening paragraph 192–437, the .hv4-pullquote
           photo band 469–625. So 34% = 437px is exactly the foot of the opening
           paragraph and 55% = 706px is just clear of the photo band. The mesh
           is at full strength behind the headline and its paragraph, and the
           whole of the ramp is spent behind and immediately below a 156px
           photograph that is opaque — the fade itself is never on show, only
           its result.

           PERCENTAGES, NOT PIXELS, because this section is 1283px at 375 and
           grows with every phone wider than that; the landmarks above move with
           it and a fixed length would not. */
        -webkit-mask-image: linear-gradient(to bottom, #000 34%, transparent 55%);
        mask-image: linear-gradient(to bottom, #000 34%, transparent 55%)
    }
}

/* margin-top 2.5rem -> 1.25rem, 09-18: "this feels like a big gap. is this
   consistent?" about the space under this section's h2. It was not consistent,
   and the audit is the answer: every other h2 on the page clears its own 20px
   bottom margin into a .hv4-lead that adds nothing, so the headline-to-copy
   distance is 20px in #hv4-problem, #hv4-cost, #hv4-conditions, #hv4-correction
   and #hv4-delivery. Here the h2's 20px collapsed against this 40px and lost, so
   this one section sat at double the page's own figure. 1.25rem makes the
   collapse a tie at 20px and the section falls in line.

   2.5rem was not arbitrary when it was written — this is the one section with no
   lead paragraph, so the h2 runs straight into a two-column grid, and a grid is
   a heavier object to drop a headline onto than a sentence is. That reasoning is
   dead now: the grid's first child is .hv4-why-standout, an italic serif line
   that reads exactly like the leads under every other h2. The headline is
   landing on a sentence here too, so it takes the sentence distance. If the
   standout is ever removed and the column opens on body copy again, this is the
   value to look at first. */
.hv4-why-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 5.5rem;
    align-items: start;
    position: relative;
    margin-top: 1.25rem
}

/* NO COLUMN DIVIDER. "let's get rid of the column divider too" (09-17), in the
   same breath as the mesh rework — and the two are one decision, not two. The
   rule was a 1px white at .25 running the full height between the tracks, and it
   had already been through four values chasing a ground that kept changing
   (--bright-blue, then a navy tint at .14 for the light ground, then white .25
   for the green, then left alone on navy).

   What it was for was separating two columns of body copy. The 5.5rem gutter
   does that on its own, and now that the mesh is blended onto the navy and
   filling the frame, a hard vertical line is the one straight edge crossing an
   artwork made entirely of curves. The `position: relative` on .hv4-why-grid is
   kept — the columns still need it — so the rule can come back by adding a
   ::before and nothing else, if it is ever wanted. Do not re-offer it.

   The @media(max-width: 991px) block below used to hide this at the collapse.
   That rule is gone with it. */

/* The columns already carry .hv4-body, so all this has to do is release the
   measure cap, which would otherwise fight the grid track. Type comes from
   .hv4-body p now that the ground is light. */
#hv4-why .hv4-why-col p {
    max-width: none
}

/* *** THE GREEN ITALIC STANDOUT AND THE RIGHT COLUMN'S DROP ARE BOTH GONE,
   09-24, AND THEY WENT TOGETHER BECAUSE THEY WERE ONE MECHANISM. ***

   What was here:

     .hv4-section--teal .hv4-body p.hv4-why-standout — leo italic,
     clamp(1.125rem, 1.7vw, 1.5rem)/1.5, --bright-green, margin 0 0 1.25rem.
     Built 09-17 from "let's take this line, put it like this in bright green",
     borrowing .pa-bio-quote (styles.css:3425), the same source the two asides
     borrow. 5.73:1 on #0e4d64, no shortfall.

     @media(min-width: 992px) .hv4-why-grid > .hv4-why-col:nth-child(2) —
     padding-top: calc(3 * clamp(1.125rem, 1.7vw, 1.5rem) + 1.25rem). From "let's
     make it so the true body copy lines up for both columns", 09-18: the left
     column opened on 24px italic and the right on 16.8px body, so the first line
     of real reading matter sat 92px higher on the right. The drop restored it,
     written as the standout's own clamp repeated verbatim so it tracked the type
     rather than freezing at 1440's 92px.

   Miro review 12.3 absorbs the standout's sentence into the new paragraph one,
   so the element it styled no longer exists. *** THE DROP HAD TO GO WITH IT, AND
   THIS IS THE PART THAT WOULD HAVE BEEN A BUG. *** A dead rule is harmless; this
   one was not dead. It measured a specific element and that element is gone, so
   left in place it pushed the right column down ~92px to line up with nothing.
   Both columns open on body copy now and start level on their own.

   *** DO NOT REBUILD EITHER ONE WITHOUT THE OTHER. *** If a standout line ever
   returns to the top of the left column, the right column needs its drop back in
   the same commit, and the drop's line count has to be re-measured — the old
   value assumed exactly two lines, verified at 992/1200/1440/1920. If it returns
   at three lines the columns are out by a line and it shows.

   See also .hv4-why-grid's margin-top note above, which cites the standout by
   name and says to re-check 1.25rem "if the standout is ever removed and the
   column opens on body copy again". Measured 09-24 after the rewrite: the h2's
   20px still collapses to a 20px tie, matching every other section. No change. */

/* THE COLOUR OVERRIDE IS REVERTED, 09-17: "this can go back how it was", on a
   screenshot of the green button. The button is plain .btn-outline-light again —
   a --dark-navy shape, white italic label, --bright-yellow arrow.

   What was tried and pulled: a --bright-green #6ce0b5 shape with a --dark-navy
   label (5.73:1 and 5.46:1, both clear AA) plus `currentColor` on the arrow,
   which was needed because `.btn-outline-light .btn-arrow` hard-codes
   --bright-yellow and that is 1.09:1 on bright green. Do not re-offer it. The
   green was asked for once ("learn more about us needs a different button
   color", when this section was navy and the navy shape was 1.4:1 on it) and
   taken back once the section went teal.

   ONE OVERRIDE STAYS, AND IT IS NOT COSMETIC. `.btn-outline-light:hover` is
   `background: var(--dark-teal)` — which since the 09-17 flip is *exactly this
   section's ground*, so an untouched button would disappear on hover. The site
   already has this problem and already has an answer: `.btn-outline-light
   .pa-bio-link:hover` in styles.css:457 goes --dark-green for the same reason on
   the practice pages' teal band. Same fix, same value. Do not delete this rule
   while the section is teal.

   FLAGGED: at rest the --dark-navy shape is 1.37:1 on --dark-teal, so the shape
   itself is barely there and the white label is doing all the work. That is the
   look that was asked for; it is not a 1.4.11 pass.

   Everything else about this button is .btn-outline-light in styles.css; the
   other thing that is this page's business is how far it sits off the paragraph
   above it. 2rem, matching the space the pull quote takes above itself in the
   other column, so the two things that interrupt the two columns are set off by
   the same amount.

   Full width below 767 for the same reason every other button on the page is —
   .btn-outline-light.fa-intro-cta does this at the same breakpoint on the facade
   pages, and #cta-learning .btn-orange does it here. A 235px button left-aligned
   under full-width copy on a phone reads as a link that failed to fill. */
#hv4-why .hv4-why-cta {
    margin-top: 2rem
}

#hv4-why .hv4-why-cta:hover {
    background: var(--dark-green);
    color: var(--white)
}

@media(max-width: 767px) {
    #hv4-why .hv4-why-cta {
        display: flex;
        width: 100%;
        justify-content: center
    }
}

/* Breaks the left column after its first paragraph. The selector is scoped to
   .hv4-why-col only to beat `#hv4-why .hv4-why-col p` above on specificity; it
   is not describing a variant.

   HISTORY — SUPERSEDED BY THE PHOTO BLOCK BELOW. Everything in the next four
   paragraphs describes the WHITE card, which the photograph replaced later the
   same day. Kept because the reasoning still explains why this is a card at all,
   but do not read any of it as the current state.

   The white card came back on 09-17: "let's have this go back how it was with
   the white box but make the line on the left yellow." It was restored exactly
   as it stood at 1399f1af — white ground, 1px #e3e7e9 hairline, 4px radius, the
   card shadow the order steps use, navy italic type — with the left rule changed
   to --bright-yellow, was --dark-green. Yellow is this section's own accent: the
   eyebrow directly above the headline is #f5b939, and so is the em in the hero
   h1 and in the closing CTA. The green it replaced was pointing at nothing in
   this band. THE YELLOW RULE SURVIVES THE PHOTO UNCHANGED.

   WHAT CAME OUT TO PUT IT BACK: the quote spent part of 09-17 as bare coloured
   type, no container — "maybe just color the text and no box?" — in
   --light-green #4abd92, and then took a clamp floor of 1.5rem when the section
   went teal under it, because #4abd92 is 3.97:1 on --dark-teal and only 24px-plus
   type clears the 3:1 large-text bar. None of that was needed on white: the type
   was --dark-navy at 15.26:1, so the floor went back to 1.25rem.

   THE ARGUMENT FOR A CARD STILL STANDS AND IS THE ONE THING TO CARRY FORWARD:
   this is the one place on the page where copy sits over a background image, and
   a container is the only treatment that makes the quote independent of whatever
   the mesh is doing. The photo changed what fills the container, not why it is
   there.

   The old flag — #f5b939 on white at about 1.9:1, under 1.4.11's 3:1 — IS CLOSED
   and does not apply to the current card. The rule now sits against the
   photograph's scrim, not white; see the measured figures in the photo block.

   Do not move this under both columns to even the column heights. That was
   tried and rejected.

   THE CAP IS 1.5rem, DOWN FROM 1.625rem, AND IT IS THE <br> THAT PAYS FOR IT.
   The markup now breaks the quote after the first sentence ("make LiminalArc on
   a new line"), which means the SECOND sentence has to survive on one line by
   itself — it can no longer borrow the tail of line one. Measured at 1440:
   "LiminalArc is that same practice, agentified." is 502px at 26px type in a
   473px inner card, so it wrapped and the quote became three lines with a
   two-word orphan. At 24px it is 463px and fits with 10px to spare.

   The rule of thumb, if the copy ever changes: this sentence needs about
   19.3x the font-size in width, and the inner card is the column less 61px
   (3px rule + 1px hairline + 2x28px padding).

   KNOWN AND ACCEPTED: this only holds down to ~1263px, where 1.9vw stops
   returning 24px and the column narrows faster than the type does. Between 992
   and 1263 the second sentence takes two lines. Below 992 .hv4-why-grid is a
   single column, the card roughly doubles, and it fits again. Going smaller
   still would buy that band at the cost of every width above it — a pull quote
   that reads at 21px is not a pull quote. If the three-line band ever has to be
   closed, shorten the sentence; do not shrink the type again. */
/* THE CARD IS A PHOTOGRAPH NOW, 09-17: "let's put this photo in the white box
   for that quote." The section's own ground and mesh are untouched — "keep the
   background we have."

   IT IS THE AERIAL FOREST ROAD, not the river, from later the same day: "can we
   use this image instead for the inset in why liminalarc." Source inset.jpg,
   2992x3992 — a PORTRAIT, into a card that is about 3.1:1.

   *** THE ROTATION IS GONE, 09-18: "this never got fixed." *** From 09-17 to
   09-18 this asset was the source turned 90° clockwise, and that is what was
   wrong with it — asked what the problem was, the answer was that it looked
   rotated. An aerial has no horizon to give it away, but the canopy does: every
   conifer throws its shadow the same way, and turning the frame turns all of
   them at once, so the light reads as coming from the side and the picture feels
   handled. The road is now vertical again, because that is how it was shot.

   WHAT THE ROTATION WAS BUYING, and it was a real thing: upright, the road runs
   straight DOWN the frame, so a 3:1 band cut from it puts a pale vertical stripe
   through the middle of the card — directly behind both lines of type. Rotated,
   the road became a horizontal S-curve, which is the shape the card is. The
   trade was a natural-looking picture against a well-placed one.

   IT IS SOLVED BY WHERE THE BAND IS CUT, NOT BY TURNING THE FRAME. The road
   wanders about 1112px horizontally between y=560 and y=1530 of the 2992x3992
   original — further sideways than the band is tall — so in that one stretch it
   crosses the frame diagonally instead of dropping through it. Cut there it
   enters at the upper left and sweeps out, and the right two-thirds of the card
   is unbroken canopy.

     python3 - <<'PY'
     from PIL import Image
     im = Image.open('<src>').convert('RGB')            # 2992x3992
     im.crop((892, 652, 2992, 1429))                    # 2100x777, 2.70:1
       .resize((1000, 370), Image.LANCZOS) \
       .save('psd_assets/why-quote-forest.jpg', quality=78, optimize=True)
     PY

   1000x370 / 96K, against the rotated crop's 165K — this stretch of canopy is
   darker and less busy, so the same quality costs 42% less. It is ~1.9x the
   card's 533x173, so it stays sharp on a retina screen, and the band is 2.70:1
   against the card's 3.09:1, which leaves slack to retune `background-position`
   vertically without going back to the source. There is no webp encoder on this
   machine.

   IT ALSO READS BETTER UNDER TYPE, which was not the reason for the change but
   settles it. Measured through the scrim below, white against the worst pixel in
   the card: 4.32:1 rotated, 4.65:1 now; at the 99th percentile, 5.70:1 against
   7.08:1. Four other bands were cut and measured (4.10, 4.36, and two that put
   the road back under the second line); this is the best of them. If the crop is
   ever moved, re-measure — the road is the only bright thing in the frame and
   where it sits IS the contrast.

   QUALITY IS NOT THE LEVER ON THIS FILE. Dense foliage has no flat areas to
   quantise, so at 1200px wide q70/q76/q82 came out 216K/231K/237K — a 12-point
   quality drop bought 9%. Size came from the PIXEL DIMENSIONS instead: 1200 ->
   1000 wide took 237K to 165K. If this ever needs to be smaller again, cut the
   width, not the quality.

   *** A THIRD PHOTOGRAPH WAS BUILT AND REVERSED, 09-18. *** The canyon river
   (AdobeStock_246097289) was cropped to its lower third for the blue water,
   installed, and taken straight back out: "let's undo the inset photo back to
   what we had." The asset is psd_assets/why-quote-canyon.jpg, 1000x363 / 88K —
   it is still on disk, uncommitted, if it is ever wanted back. It needed a
   VIGNETTE scrim rather than this one-way fade, because its bright parts are the
   two rock walls at the edges and its dark part is the water between them, which
   is the opposite of this crop. If it does come back, the ramp comes back with
   it — see the method note on the ::before rule. Two earlier river crops
   (AdobeStock_238420412, psd_assets/why-quote-river.jpg) were also reversed.

   THE SCRIM IS NOT OPTIONAL AND IT IS NOT A VEIL ON A BRAND COLOUR. The
   standing rule is never to darken a brand-colour card to rescue contrast — fix
   the type and flag the shortfall. That rule is about flat brand grounds, where
   the colour IS the decision. A photograph is different: it has no single
   luminance, so no type colour can be chosen against it. This one runs from
   near-black canopy shadow to pale gravel road inside the 125px the card
   occupies. Neither white nor navy is rescuable by choosing better. The scrim
   is what gives the type a ground at all.

   It is a BLACK gradient as of 09-17 — "what if this stays in color... if it
   needs an overlay make it black" — not a navy one, and not a flat wash. Navy
   ran .88/.74/.58, then .86/.70/.52 ("this needs a little more love"), and both
   were dense enough that the photograph was only tinting a navy box, which is an
   expensive way to draw a navy box. Black lets the alphas come down instead of
   the hue come up. The stops and the contrast arithmetic are on the ::before
   rule below — that is where the numbers live, because they are the values that
   change when the picture does.

   THE YELLOW LEFT RULE IS GONE, 09-17: "ditch the yellow line." It was there to
   pick up the WHY LIMINALARC eyebrow directly above it, and that eyebrow is now
   --bright-green, so the rule was the last yellow in the band and it was
   pointing at nothing.

   NOTHING REPLACES IT. A 1px rgba(255,255,255,.22) hairline was run all the way
   round as the card's new edge and rejected the same day: "this outline is
   weird." It was reasoned from "a dark photo on a dark band needs an edge or it
   dissolves," and that reasoning is wrong here — the photograph is not uniformly
   dark, so the hairline read at full strength against the darkest part of the
   picture and vanished against the lightest. An edge that only exists for part
   of its length looks like a rendering fault, not a frame.

   The edge is the box-shadow, which is the same soft lift the white card had
   before the photo went in, and it does not have to be even against the
   photograph because it is outside it. Do not re-add a border, an outline, or a
   one-sided rule to this card. If it ever does dissolve into the band, deepen
   the shadow.

   The padding does stay where the hairline pass put it, 1.75rem/2rem → 2.25rem,
   for the same reason the scrim eased: the photograph needs margin around the
   type or the card reads as a caption strip rather than as an image. */
#hv4-why .hv4-why-col .hv4-pullquote {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    margin: 2rem 0 2.25rem;
    padding: 2.25rem;
    background: var(--dark-navy) url("psd_assets/why-quote-forest.jpg") center/cover no-repeat;
    border-radius: 4px;
    box-shadow: 0 1px 2px rgba(6, 41, 53, .06), 0 6px 18px rgba(6, 41, 53, .18);
    font-family: "leo", sans-serif;
    font-size: clamp(1.25rem, 1.9vw, 1.5rem);
    font-style: italic;
    font-weight: 500;
    line-height: 1.4;
    color: var(--white);
    max-width: none
}

/* The scrim. A `to right` ramp, heaviest at the left edge where every line
   starts and lightest at the right where none of them reach. Kept as a ::before
   at z-index -1 rather than a gradient stacked in the `background` shorthand, so
   the photo's `background-position` stays a single value anyone can retune
   without rewriting three colour stops.

   *** IT IS BLACK, NOT NAVY, AND THAT IS THE WHOLE POINT OF THIS VERSION.
   09-17: "what if this stays in color... if it needs an overlay make it black."
   Navy at rgba(6,41,53,…) does not just darken a photograph, it TINTS it: at the
   .70-.86 the old ramp ran, the forest's greens were pulled all the way to the
   section's own teal and the card read as a navy rectangle with foliage texture
   in it. Black has no hue to donate, so every alpha below costs brightness only
   and the greens survive at their own hue. Do not "harmonise" this back to a
   brand colour — a brand-coloured scrim is what was just removed. ***

   THE FLAT FLOOR IS GONE with the navy. It existed to hold the river crop's
   blown-white foam and had already come down .28 → .12; with a black ramp there
   is nothing left for it to do that the ramp's own left end does not do better,
   and a second layer only made the two harder to retune together.

   THE ALPHAS, and the method, which is the part to reuse. Find the brightest
   thing THE WORDS CROSS, not the brightest thing in the picture. The rotated
   crop puts the gravel road's S-curve straight through the type band, and the
   road measures about .47 luminance. White needs the ground at .1833 or darker
   for 4.5:1. Black compositing is not linear in alpha — the result is roughly
   L x (1-a)^2.2 — so:

     0%    .52  ->  .47 x .199 = .094   ->  7.3:1
     45%   .42  ->  .47 x .302 = .142   ->  5.5:1   (road at its brightest here)
     62%   .383 ->  .47 x .346 = .163   ->  4.9:1   (longest line ends here)
     100%  .30  ->  .47 x .456 = .215   ->  4.0:1   (no type, nothing set here)

   So the floor under the type is about 4.9:1 and the right third is deliberately
   left light enough to read as a photograph. These are LOWER alphas than the
   navy ramp they replace, which is why the colour comes back.

   *** IF THE PHOTOGRAPH EVER CHANGES, RE-DERIVE THIS RAMP. DO NOT CARRY IT
   OVER. *** The alphas are a property of the picture, not of the card. Invert
   the arithmetic above and the required alpha at any column is
   1 - (.1833/L)^(1/2.2); run it per column across the crop, over only the rows
   the type occupies, at about the 93rd percentile of luminance so one glint does
   not set the whole ramp, then take a running max over neighbouring stops
   because a gradient interpolates between them. When the canyon crop was tried
   on 09-18 it measured bright at BOTH EDGES and dark in the middle — the exact
   opposite of this road — and this ramp reused unchanged would have left its
   right-hand rock ledge at about 2.6:1 under the last word of the longest line.

   THE PARENT MUST STAY `isolation: isolate`. z-index: -1 on a ::before paints
   BELOW the parent's own background only if it is not isolated — isolated, it
   paints between the background and the text, which is exactly where a scrim
   belongs. Remove the isolation and the scrim disappears behind the photo. */
#hv4-why .hv4-why-col .hv4-pullquote::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to right, rgba(0, 0, 0, .52) 0%, rgba(0, 0, 0, .42) 45%, rgba(0, 0, 0, .30) 100%);
    pointer-events: none;
    z-index: -1
}

/* *** THE ROW GAP IS NOT ZERO AND IT MUST NOT GO BACK TO ZERO. *** The
   shorthand `gap: 0` was here to kill the desktop's 5.5rem COLUMN gap, which has
   nothing to do once there is one track — but it set the ROW gap too, and at one
   track the row gap is the seam between the two columns' copy.

   .hv4-body p ends on `margin-bottom: 0` for its last child, so column one's
   last paragraph had nothing under it and column two's first paragraph had
   nothing over it. Measured 09-25 at both 375 and 768: paragraph two's bottom
   and paragraph three's top were the SAME y. Two paragraphs printed as one
   block, with every other break in the section at 19.2px.

   1.2rem is that 19.2px — .hv4-body p's own margin-bottom, so the seam between
   the columns is indistinguishable from a paragraph break inside one. Write it
   as `column-gap` + `row-gap`, never the shorthand, so killing one cannot kill
   the other again. If .hv4-body p's spacing ever changes, this tracks it by
   hand: re-measure, do not assume. */
@media(max-width: 991px) {
    .hv4-why-grid {
        grid-template-columns: minmax(0, 1fr);
        column-gap: 0;
        row-gap: 1.2rem
    }
}

/* ==========================================================================
   10. Closing CTA — a recolour and one gap in the shared component.

      The page closes with the shared #cta-learning band (navy left half, photo
      right, full-bleed 50/50), the same one every practice-area page closes
      with. Its structure lives in styles.css:3622-3733 — grid, min-height, type
      scale, padding, button shape, photo cover — and all of that is inherited
      untouched. The markup sits outside the .hv4 wrapper in the template, which
      is what stops `.hv4 h2` from beating `.cta-learning-text h2` on source
      order.

      WHAT THIS PAGE CHANGES, 09-17: "let's have cta look more like this", with a
      reference showing the green wave running down into a DARK NAVY panel, the
      italic second line in GOLD, and a BLUE button. Three colour values; nothing
      structural.

      Why it is safe to do here rather than in styles.css: this file is enqueued
      only on template-homepage-v4.php (functions.php:631). Every practice-area
      page keeps the bright-blue band it has today. The selectors below are
      deliberately no more specific than they need to be — `#cta-learning` is
      (1,0,0), the same as the rule it beats in styles.css, and it wins on source
      order alone because homepage-v4.css is enqueued after it. If the two files
      are ever concatenated in the other order, these stop working silently.

      The recolour CLOSES a flag rather than opening one. The paragraph below was
      white .9 on #3499e7 at 2.9:1, under AA; on --blue it is 5.5:1. The white h2
      is 6.2:1 and the gold em 3.51:1, which clears the 3:1 that applies to it as
      large text. The one shortfall left is the button SHAPE — --dark-navy on
      --blue is 2.46:1, under 1.4.11's 3:1 for a control boundary, though its
      white label is 15.3:1. Flagged, not fixed: both ways to buy the ratio cost
      more than they return. See the button note below.

      This page runs its own copy in the band — "Start With a Conversation" over
      the paragraph about gates, carried back from the #hv4-cta section this
      replaced ("that footer cta is correct but it needs the new copy from what
      was there"). The paragraph is the reason there is any CSS here at all: no
      other page puts body copy in this band, so styles.css never had to style
      one, and an unstyled <p> would fall back to the theme's default.

      Deleted from here: the navy #hv4-cta band. It carried
      Updated-Workshop-CTA.webp sized off a --la-content-width custom property
      and bled off the right edge under a linear-gradient mask, with a 520px
      .hv4-cta-text column, a text-shadowed paragraph (the photo ran under the
      copy), and a <=1100 fallback that swapped the mask for a two-gradient
      scrim plus a 48px grid. All of it is gone rather than commented out; git
      has it at 5a69285e if the band is ever wanted back.
   ========================================================================== */

/* The ground: --dark-navy #062935.

   THE PAIR WAS SWAPPED, 09-17: "let's flip. cta is navy and the section above
   is the teal". This band and #hv4-why directly above it exchanged grounds —
   #hv4-why was navy and is now --dark-teal, this was --dark-teal and is now
   navy. They are a PAIR and have to move together: the whole point of either
   value is that the two adjacent dark bands are not the same dark, so changing
   one alone puts the page back to navy-on-navy with nothing between them.

   Five values in one afternoon — --bright-blue → --dark-navy → --blue →
   --dark-teal → --dark-navy. The teal step is worth keeping straight rather
   than deleting, because it is the obvious thing to try again and it is now
   spoken for one section up.

   WHAT THE FLIP COSTS AND PAYS. Navy is DARKER than --dark-teal, so everything
   white gets better again and the orange button gets its 3:1 back:

     h2 white                    9.32 → 15.26:1
     paragraph white .9          8.10 → 13.30:1
     h2 em --bright-yellow       5.28 →  8.61:1
     button shape --bright-orange 2.89 →  4.78:1  — the 1.4.11 shortfall closes

   The orange button clearing 3:1 is the real gain: it was the last flagged
   contrast failure in this band, and the flip closed it without touching the
   button. */
#cta-learning {
    background: var(--dark-navy)
}

/* NO WAVE ON THE TOP of this band, and that is still true — but there IS one on
   the bottom now; it is built in section 10b and this note is only about the
   seam above. One was built here on 09-17 — an absolutely positioned
   .hv4-cta-wave overlaying the top 70px of both columns, because a curve and a
   photo cannot touch while they sit in different sections — and it was taken out
   the same day: "let's undo the wave here". The rule and its SVG are both gone;
   git has them if the seam is ever wanted back.

   The wave inversion rule this page runs on (each path fills its own section's
   colour, the CSS background supplies the neighbour's) assumes the neighbour is
   ONE colour. This band is two — a flat ground on the left, a photograph on the
   right — so neither of its boundaries can take a wave built that way. The
   bottom seam got one anyway on 09-18 by inverting the construction rather than
   the fill: see 10b. THAT TRICK WOULD NOT WORK HERE. It works below because the
   band above a wave can be overlaid, and the section this one would have to be
   drawn from is #hv4-why, whose own .wave-top already occupies that edge. */

/* The photo's own fallback colour, which shows in the sliver the cover crop
   cannot fill and for as long as the JPEG is in flight. It was --bright-blue to
   match the old ground; left alone it would flash a blue half-band on load. It
   tracks the ground above — one value, two places. */
#cta-learning .cta-learning-image {
    background-color: var(--dark-navy)
}

/* THE CROP CUTS THE SECOND CLIMBER IN HALF, 10-01. "photo crop is wrong" on an
   iPad capture, then "only the widths that are broken" when asked how far it
   should reach.

   ⚠️ `background-position: center` ON THE STANDARD IS A NO-OP, NOT A FRAMING
   DECISION, and that is the whole licence for this rule. Measured on
   /practice-area/ai/ at 1100: its text column is 340px, so the photo box is
   550x340 — WIDER in aspect than the photograph — cover binds on the width and
   it crops 0% horizontally. `center` there has nothing to centre. Ours is
   550x593 at the same width and throws away 38%.

   THE 253px IS COPY, NOT STYLING. Same component, same headline height (101px,
   two lines, 44px), same button. The standard's CTA has NO PARAGRAPH; ours
   carries a 190px five-line one, and the wave needs 55px more bottom padding
   than the standard does (styles.css gives this band 77/77; we run 77/132, see
   the clearance note further down). Those two are the entire difference, they
   make the box portrait, and the crop follows.

   ⚠️ THE PARAGRAPH IS DELIBERATE AND LOAD-BEARING. Put to Jess 10-01 as a
   straight choice against dropping it to match the standard — "keep the
   paragraph", because homepage 4 is a longer argument and its close earns a
   sentence the practice-area pages don't need. DO NOT "tidy" it away for
   consistency; it is the considered divergence and this rule is its cost. If it
   ever does go, the box lands near the standard's and this rule becomes a no-op
   — re-measure before assuming it still earns its place.

   A FOCAL POINT IS A PROPERTY OF THE PHOTOGRAPH, NOT OF THE COMPONENT. All 21
   templates set their image inline on this element and only ours uses
   cta-learning-climbers.jpg; the other 20 share three files. homepage-v4.css is
   enqueued solely on template-homepage-v4.php (functions.php:633), so this
   cannot reach them.

   ⚠️ THE BOX HEIGHT IS NOT MONOTONIC WITH WINDOW WIDTH, so the fault is TWO
   bands with a sound stretch between them and does not simply clear above a
   threshold. At 1025 the standard's flat `padding-block: 2.5rem` tablet rule
   (styles.css:3696) ends and `clamp(3rem, 7vw, 5rem)` resumes on BOTH edges
   while the column is still narrow — the box goes 496x422 at 991 to 513x603 at
   1025 and the crop tightens again. Subjects span source x 750-1338 of 1600
   (bounding-box on the warm pixels — the lead climber's gold pack and the
   second climber's orange jacket — not by eye). `rightOf` = source px standing
   to the right of them at the inherited 50%:

       375    box 375x240   window 1600   +262   whole photo, position inert
       768    box 384x466   window  878    -99   SLICED
       850    box 425x473   window  956    -60   SLICED
       930    box 465x448   window 1106    +15   just clears
       991    box 496x422   window 1250    +87   sound
       1025   box 513x603   window  906    -85   SLICED AGAIN
       1100   box 550x593   window  988    -44   SLICED
       1200   box 600x608   window 1051    -13   SLICED, marginally
       1280   box 640x588   window 1159    +41   sound
       1440   box 720x568   window 1349   +136   comfortable

   ONE BLOCK AND NOT TWO. On the ladder the broken bands are 768-991 and
   1025-1199, which are contiguous, so this is 768-1199 — one seam to notice
   while resizing instead of three. 930-1024 is swept in and costs nothing (75%
   measures +139 to +175 there, sound either way). The true clear point is ~1215,
   so 1200-1215 keeps about 13 source px of slice; 1199 is the ladder's rung and
   that residue is four rendered pixels.

   75% AND NOT MORE. It holds the whole range: rightOf +81 at its worst (768)
   rising to +199 at 1440, and leftOf never below 208, so both climbers stay in
   frame at every width in the block AND above it. 70% was the first try and
   leaves the second climber 21 source px — in frame by arithmetic and still
   reading as trimmed. Past 80% the left rock face disappears and the band stops
   being a photograph of a climb.

   RE-DERIVE ALL OF THIS IF THE PHOTO IS REPLACED OR THE CTA COPY CHANGES. Every
   number above is a coordinate in cta-learning-climbers.jpg at a box height
   that the paragraph sets. */
@media(min-width: 768px)and (max-width: 1199px) {
    #cta-learning .cta-learning-image {
        background-position: 75% center
    }
}

/* THE BUTTON HAS TO BE OVERRIDDEN, and this is not a style preference — the
   shared component paints this button `background: var(--dark-navy)` at rest,
   which after the 09-17 flip is EXACTLY the ground. Left alone the button would
   be an invisible rectangle with a white label floating on it, at 1:1.

   The hazard simply moved sides. Before the flip it was the HOVER state that
   collided (the component hovers to --dark-teal, which was the ground then, also
   1:1) and the rest state was merely bad at 1.66:1. Now rest is the collision
   and hover is safe. Either way the override stays — that is the point of the
   note below.

   These overrides were deleted a few hours earlier, when the ground was --blue
   and the component's navy button was correct again. They are back because the
   ground moved, which is the whole hazard with this band: it is a SHARED
   component, so every recolour of the ground has to be checked against two
   button states as well as the type.

   ORANGE, 09-17: "let's try the get in touch button orange." --bright-orange
   #f46036, which is .btn-orange's OWN value in styles.css — so this override is
   really just cancelling the #cta-learning override that turns the site's orange
   button navy. This band is the page's only call to action and orange is the
   brand's action colour, so it is the one place on the page the accent belongs
   most; everywhere else --bright-orange is spoken for (the readiness-gap measure,
   the hurdle line).

   It briefly went --bright-blue instead, on the same afternoon, purely because
   that cleared 3:1 against the ground. Orange was asked for over it.

   THE HOVER OVERRIDE IS GONE, 09-18: "this should go teal on hover." There is
   no rule here for it any more, because --dark-teal is ALREADY what .btn-orange
   hovers to site-wide (styles.css:424). What was here was a white-on-navy hover,
   and deleting it is what restores the component's own behaviour — adding a rule
   that re-states --dark-teal would just be a second copy of the same value to
   keep in step. So: do not "restore" a hover rule in this block. If it is
   missing, that IS the setting.

   *** SHORTFALL, CLIENT DECISION: --dark-teal #0e4d64 on this band's --dark-navy
   #062935 ground is 1.63:1. The LABEL is fine — white stays white at 12.0:1 — but
   the button's SHAPE all but disappears into the ground on hover, and 1.4.11 asks
   3:1 of the boundary that identifies a control. This is the exact hazard the
   paragraph above describes from the other direction: it was flagged when the
   ground was teal and the hover was the collision, then the ground flipped to
   navy and the white hover made it safe. Asking for teal back re-opens it. The
   routes out are a white or --light-gray hover (15.26:1), or a 1px light outline
   that only appears on hover. Do not silently re-add the white hover. ***

   ONE SHORTFALL LEFT on the resting state, and it is not this page's: the white
   label on orange is 3.21:1 at 13.6px, under AA. White on --bright-orange is what
   .btn-orange is site-wide, so changing it here would make this the only button
   on the site that reads differently. Raised with the client.

   The resting shape's own contrast is not flagged. --bright-orange measured
   2.89:1 against --dark-teal, a hair under 1.4.11's 3:1; against navy it is
   4.78:1. The flip fixed it for free. */
#cta-learning .btn-orange {
    background: var(--bright-orange)
}

/* The italic second line. On every other page this em is white and the break
   exists only to put the turn of the sentence in the emphasis face; here the
   colour does that work too, which is why the authored <br> in the template
   still matters — "a Conversation" has to be the whole of its own line for a
   second colour on it to read as emphasis rather than as an error.

   --bright-yellow is the only warm value in the palette that reads on this
   ground: 3.51:1 on --blue, which clears the 3:1 that applies because the h2 is
   large text (clamp(2rem, 4vw, 3.4rem) — never below 32px). It was 8.6:1 while
   the band was navy, so the ratio fell with the recolour and is now sitting on
   the large-text allowance rather than comfortably above AA. --bright-orange
   would be worse and is already spoken for on this page: it is the readiness
   gap's accent and the hurdle line's, and a third job would stop it meaning
   anything.

   It is also, measured, the exact colour the hero's own italic line already
   carries — `.hv4 h1 em` computes rgb(245,185,57).

   ALL WHITE AGAIN, 09-17: "make the start with a conversation text all white".
   The gold is out and this rule is a no-op left standing on purpose, because
   "open and close on the same gold italic" is a good argument that will be made
   again. What changed underneath it is the ground: the band went --dark-teal in
   the same batch, and the section immediately above it is navy with a --bright-
   yellow eyebrow, so the gold was being asked to do the same job twice in a row.
   White at 9.32:1 is also the strongest the headline has measured on any of this
   band's four grounds.

   The <br> in the template still matters and is unrelated to colour: "a
   Conversation" has to be the whole of its own line for the italic to read as a
   turn rather than as a stray emphasis. Do not remove it. */
#cta-learning .cta-learning-text h2 em {
    color: var(--white)
}

/* The button's own rules are further up, with the ground they answer to. The
   short history, because it has turned over three times in a day and the middle
   state keeps looking like the right one: on --bright-blue the shared component
   was fine; on --dark-navy the button went --bright-blue with a --blue hover; on
   --blue both of those were wrong (--bright-blue is 2.02:1 on it and the hover
   value IS it) so the overrides came out entirely; on --dark-teal they had to
   come back, because the component's HOVER value is --dark-teal.

   The rule to carry forward: this band's button has to be re-checked in BOTH
   states every single time the ground moves. It is a shared component and it is
   the only control on the page. */

/* Sits in the h2's 2rem bottom margin rather than adding to it: the h2 keeps its
   margin and the paragraph takes 1.5rem of it back, so the copy reads as the
   headline's second beat and the button still sits 2rem clear of the block.

   Measure is inherited, not set. The text half is 720px at 1440 and the
   component's own padding (9.9vw a side) leaves 435px, about 62 characters of
   1.0625rem serif — already the right measure, so capping it would only be a
   second number to keep in sync.

   White at 90%, not flat white. The h2 and the button are what have to be read
   from across the room; solid white would give the paragraph their weight. On
   the old bright-blue ground that was 2.9:1 and flagged; on --blue it is 5.5:1,
   so the transparency is now doing only the job it was meant to do and still
   clears AA. */
#cta-learning .cta-learning-text p {
    margin: -1.5rem 0 2rem;
    font-family: "leo", serif;
    font-size: 1.0625rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, .9);
    text-wrap: pretty
}

/* The h2's own bottom margin drops to 1.5rem below 1024 and 1.75rem below 768,
   so the pull-up has to come down with it or the paragraph sits on the
   headline's descenders. */
@media(max-width: 1024px) {
    #cta-learning .cta-learning-text p {
        margin: -1rem 0 1.75rem;
        font-size: 1rem
    }
}


/* ==========================================================================
   10b. Insights — the shared carousel, called as partials/part-insights.php.

        Everything about it is inherited from styles.css. The one thing that
        does not transfer is its opening wave: the partial fills that path with
        #062935 so it reads as the dark navy section above it running down into
        the light ground. On the original homepage that section is #workshop,
        which is exactly --dark-navy. Here the section above is #cta-learning —
        and as of 09-17 its left half is --dark-navy too, so half of that wave
        would now be correct. The other half is a photograph.

        THE PARTIAL'S OWN WAVE STAYS HIDDEN. There is no single colour that can
        fill it, because the band it would have to continue is two different
        things across its own width, and a wave that continues the left half and
        cuts the right one off mid-photograph reads as a bug.

        THE WAVE IS DRAWN FROM THE OTHER SIDE INSTEAD, 09-18: "any chance we can
        add a wave to the top of insights. make sure you fold in the image above
        in the cta to crop into the wave."

        That instruction is the answer to the problem above. The partial's wave
        fills the colour of the section ABOVE and sits on top of the one below;
        this one fills the section BELOW and sits on top of the one above. Filling
        downward needs one colour for the whole width — #insights' own ground — so
        the two-things-across-its-width problem disappears, and the navy and the
        photograph both get cut by the same curve. The photo cropping into the
        wave is not a side effect, it is the requirement.

        It hangs off #cta-learning, NOT #insights, and that is forced: #insights
        is `overflow: hidden`, so anything on it with a negative offset is clipped
        before it reaches the band above. #cta-learning is overflow: visible and
        only needed `position: relative` to host the pseudo.

        TEXTURE FROM THE ELEMENT, SHAPE FROM A MASK — the same construction as
        `#hv4-why .wave-top` at the top of this file, and for the same reason: a
        stretched viewBox smears a fill pattern, so the noise has to come from the
        background and only the mask is allowed to stretch. The background here is
        .hv4-section--light's three layers verbatim, because that is what #insights
        computes to (rgb(251,251,251) under a 20% wash under the tile). If the
        insights ground ever moves, this moves with it — they are one surface.

        THE PATH IS THE PARTIAL'S CURVE CLOSED DOWNWARD. Same control points,
        `L1440,90 L0,90 Z` instead of `L1440,0 L0,0 Z`. Both files have to change
        together if the curve is ever redrawn.

        It OVERLAYS rather than adds height, so the CTA band does not grow and
        #insights keeps the 5rem top padding below — the wave is not providing the
        clearance here, the padding is. On the original homepage the wave carried
        `margin-bottom: 3.5rem` and #insights had no top padding of its own; that
        is the arrangement this replaces.

        38px under 992 to match styles.css:2427, which drops every other wave on
        the site to that height at the same breakpoint.
   ========================================================================== */

#cta-learning+#insights .insights-wave-top {
    display: none
}

#cta-learning {
    position: relative
}

#cta-learning::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 70px;
    background: linear-gradient(rgba(251, 251, 251, 0.2), rgba(251, 251, 251, 0.2)), url("psd_assets/noise-tile.png") repeat, var(--light-gray);
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 90' preserveAspectRatio='none'><path d='M0,55 C180,25 360,15 540,22 C720,30 900,55 1080,70 C1260,80 1350,68 1440,55 L1440,90 L0,90 Z' fill='white'/></svg>") no-repeat 0 0/100% 100%;
    mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 90' preserveAspectRatio='none'><path d='M0,55 C180,25 360,15 540,22 C720,30 900,55 1080,70 C1260,80 1350,68 1440,55 L1440,90 L0,90 Z' fill='white'/></svg>") no-repeat 0 0/100% 100%;
    pointer-events: none;
    z-index: 2
}

@media(max-width: 991px) {
    #cta-learning::after {
        height: 38px
    }
}

/* THE BAND OWES THE WAVE ITS BOTTOM PADDING BACK, 09-18: "this padding feels
   off. the top has a bunch of room and the bottom has none."

   The band was symmetrical and still is in the box model — .cta-learning-text
   is 80px top and 80px bottom, so the h2 starts 80px down and the button ends
   80px up. Then the wave above was laid over the bottom 70px of it. 80px of
   clearance with 70px of curve painted on top of it is 10px of clearance, which
   is what the crop shows: the button all but resting on the curve while the
   headline has its full 80px.

   This is the page's standing wave rule applied to the other end. Padding is
   measured from the HIGHEST point of the wave, not from the section edge, so it
   has to clear where the curve first intrudes rather than where the band stops.
   The mask path bottoms out at y≈19.85 of its 90-unit viewBox (the cubic's
   minimum, near x=432), so the fill reaches 78% of the wave's height above the
   section's bottom edge. .78 * 70 = 55px, and 80 + 55 = 135px restores the 80px
   the button is supposed to have. The .78 is a property of the PATH — redraw the
   curve and this coefficient is wrong, so recompute it there.

   ⚠️ THIS USED TO SAY "≥992 ONLY, AND THAT IS NOT LAZINESS — BELOW 992 THE BAND
   STACKS". IT STACKS BELOW 768, NOT BELOW 992, AND THE WRONG NUMBER COST THE
   WHOLE TABLET BAND. `.cta-learning-grid` goes to `1fr` in styles.css:3708,
   which is a ≤767 rule; from 768 to 991 the band is still two columns, the
   button is still the last thing in the left one, and the wave still cuts
   across it. The test is grid-template-columns, measured, not the breakpoint
   read off the rule: `375px` at 375 and `500px 500px` / `720px 720px` at 1000
   and 1440. ⚠️ Do NOT test this by comparing the photo's bottom edge to the
   button's — in a two-column grid the photo stretches to the row's full height,
   so it is the lower element at EVERY width and the comparison answers "photo"
   whether the band is stacked or not. That detector looks like it works.

   THE RULE IS NOT A NUMBER, IT IS visible-bottom == visible-top, AND IT HAS TO
   BE STATED FOUR TIMES BECAUSE THE TWO TERMS STEP ON DIFFERENT RUNGS. The
   shared component's padding-block changes at 1024/768 (styles.css:3643/3697)
   and this page's wave changes height at 991, so no single band shares both:

                      pad-top   wave   ink(.78)   bottom      was
       ≥1025       clamp 3-5rem   70       55      top+55     135
       992-1024       2.5rem=40   70       55          95     135   <- +40 OVER
       768-991        2.5rem=40   38       30          70      40   <- 30 SHORT
       ≤767        clamp 3-5rem   38       30     untouched    —

   Measured before and after at four widths, visible top / visible bottom:
   768 was 40/10.4 and is 40/40.4. 1000 was 40/80.4 and is 40/40.4. 1440 is
   80/80.4, unchanged — `calc(clamp(3rem,7vw,5rem) + 55px)` is 135px there by
   construction. It is a calc rather than the flat 135 so that it tracks the
   clamp through 1025-1142, where 7vw is still climbing and the old flat value
   over-paid by up to 3.4px.

   *** THE TARGET IS liminalarc.co AND IT IS MEASURED, NOT INFERRED. *** "look
   at liminalarc.co because all the ctas should be consistent." On
   /practice-area/ai/ this same component computes `#cta-learning::after` to
   `content: none` — THE OTHER PAGES HAVE NO WAVE OVER THIS BAND AT ALL, so it
   runs a clean symmetrical 40/40 at 768 and 80/80 at 1440. The wave is a
   homepage-4 invention (section 10b above), so the asymmetry was ours alone and
   "consistent with the standard" means restoring the standard's VISIBLE figure
   in each band, not copying its padding values.

   ≤767 IS DELIBERATELY ABSENT AND THAT PART OF THE OLD NOTE WAS RIGHT. There
   the grid is one column with `.cta-learning-image { order: 2 }`, so the photo
   is the last thing in the band and the wave cuts the photograph, never the
   type. Padding here would only open navy between the button and the picture.
   Confirmed at 375: the element under the wave is the photo, and the button
   already stands 258px clear of it. */
@media(min-width: 1025px) {
    #cta-learning .cta-learning-text {
        padding-bottom: calc(clamp(3rem, 7vw, 5rem) + 55px)
    }
}

@media(min-width: 992px)and (max-width: 1024px) {
    #cta-learning .cta-learning-text {
        padding-bottom: calc(2.5rem + 55px)
    }
}

@media(min-width: 768px)and (max-width: 991px) {
    #cta-learning .cta-learning-text {
        padding-bottom: calc(2.5rem + 30px)
    }
}

/* 5rem -> 41px, 09-18, because 5rem was measured from the wrong line.

   THE WAVE ABOVE IS FILLED WITH THIS SECTION'S OWN GROUND, so the light surface
   does not begin at #insights' top edge — it begins wherever the curve's highest
   point is, which is inside #cta-learning. That is the page's standing wave rule
   (padding is measured from the highest point of the wave, not the section
   edge), and this section was the one place still ignoring it: the wave was
   written as an overlay that "does not provide the clearance", which is true of
   the BOX and false of the EYE.

   THE ARITHMETIC. The mask path bottoms out at y≈19.85 of its 90-unit viewBox,
   so the fill reaches (1 - 19.85/90) = 78% of the wave's height above the
   boundary: .78 * 70 = 55px of light ground already standing before the section
   starts. 41 + 55 = 96px, which is #hv4-correction's opening exactly — the
   page's figure, and the one the user asked to match. Measured, not derived:
   headline top minus curve top reads 96 at 1440.

   Below 992 the wave drops to 38px (styles.css:2427 does this site-wide), so the
   same 78% is only 30px and the padding has to be 34 rather than 41 to land on
   the 4rem this page opens its mobile sections with. TWO NUMBERS, ONE RULE — if
   the curve is ever redrawn, the .78 changes and both of these are wrong.
   Recompute from the path, do not scale them. */
#cta-learning+#insights {
    padding-top: 41px
}

@media(max-width: 991px) {
    #cta-learning+#insights {
        padding-top: 34px
    }
}

/* ==========================================================================
   11. Global responsive
   ========================================================================== */

@media(max-width: 991px) {
    .hv4-section {
        padding: 4rem 0
    }

    .hv4-lead,
    .hv4-order-close {
        max-width: 100%
    }
}

@media(max-width: 991px) {
    /* *** THE AUTHORED LINE BREAKS ARE DESKTOP TYPOGRAPHY AND THEY COME OUT ON
       PHONES. *** Six headings on this page carry a hand-placed <br> that
       splits them at a sentence or a clause — "Your Pilots Aren't
       Failing.<br>They're Revealing What's Wrong." Those breaks were set
       against a desktop measure. On a 330px phone column the text already
       wraps two or three times on its own, so the <br> lands mid-thought and
       adds a short ragged line on top of it: 09-25's "Your Pilots / Aren't
       Failing. / They're Revealing / What's Wrong."

       Jess, 09-25: "all titles can go across the page and we don't need to
       wrap text by sentences." So the breaks are suppressed rather than
       removed from the template — desktop still wants them, and deleting them
       from the markup would lose them there.

       ≤991, not ≤767, and that is the standard's breakpoint rather than a
       choice made here: styles.css:332 does exactly this for
       `.hero-services .service-name br` at ≤991. Tablet is already narrow
       enough for a desktop sentence break to land wrong.

       ⚠️ SUPPRESSING THE <br> IS ONLY HALF THE INSTRUCTION AND THE OTHER HALF
       WAS ANSWERED BACKWARDS FOR FIVE DAYS. Taking the break out lets the
       heading go across; `text-wrap: balance` then puts it straight back into
       a narrow block. Balance does not fill a line, it equalises lines — it
       finds the NARROWEST width that still fits the same number of lines and
       sets every line to that. So a heading that greedy-wraps to 2 lines
       balances to 2 lines of equal, short length, and the result is exactly
       the shape 09-25 was complaining about, arrived at from the other side.

       09-30, three screenshots in a row: "this should be going across on
       mobile", "same here to should def fit on the first line", "same here
       everything goes across". Measured at 375, first-line width against a
       330px column:

         The Spend Compounds...     271 -> 320
         Your Pilots Aren't...      283 -> 289, and 3 lines instead of 3 ragged
         Four Conditions You Can... 233 -> 283
         How to Get Production...   259 -> 287
         We Built These...          302 -> 302 (already full)

       `pretty`, NOT `wrap`, and the difference is the standing "no orphans"
       rule. Plain `wrap` is greedy: it fills every line and dumps whatever is
       left on the last one, which strands "Wrong." (83px) under Your Pilots
       and "Days" (70px) under the hero h1. `pretty` fills the line the same
       way and only pulls back when the LAST line would be short — the two
       instructions are not in conflict, they are the two halves of what
       `pretty` does. It is also the value styles.css:59 already runs on every
       paragraph site-wide, so this is the page's own idiom, not a new one.

       ⚠️ THE ONE HEADING THIS DOES NOT MOVE is "One Slice, Ninety Days,
       Repeat" — 197/145 under both balance and pretty. Filling it needs
       264/78, i.e. the orphan. The whole string is 349px against a 330px
       column, so it misses one line by 6%. Do not chase it with a smaller h2:
       the size is shared with six other headings that are all correct.

       The h2s and h3s need the selector because styles.css:53-57 sets balance
       on the bare elements site-wide; `.hv4 h2` is (0,1,1) against that
       (0,0,1), so the page overrides itself here and nowhere else. */
    /* ⚠️ `#hv4-hero h1 br` CAME OUT OF THIS LIST ON 10-01 BECAUSE THE <br> IS
       GONE FROM THE MARKUP, NOT BECAUSE THE HERO STOPPED NEEDING IT. The hero
       went up a size that day and the two authored breaks stopped fitting on one
       line each, so above 991 — where this rule does not reach — each clause
       wrapped again and the break landed on top of a wrap. Five lines at 1024,
       "Time," and "Days" alone. Hiding them at one more width would have fixed
       992 to 1280 and not 1281 to 1365, so they were deleted from the template
       instead; the long note there carries the measurements. A selector for an
       element that no longer exists is not harmless — it reads as evidence that
       the hero still has breaks to suppress. */
    .hv4 h2 br,
    .hv4-aside-title br {
        display: none
    }

    /* *** #hv4-cost KEEPS ITS AUTHORED BREAK BELOW 991. *** 10-01: "let's put
       'the' on the second line", on "The Spend Compounds. The Value Doesn't."

       Above 991 the <br> is live and the heading already reads as two clauses,
       one per line. The rule above was hiding it below 991, and the greedy/
       balanced wrap that took over split the SECOND clause instead:

         hidden   "The Spend Compounds. The" / "Value Doesn't."
         shown    "The Spend Compounds."     / "The Value Doesn't."

       The first version breaks the sentence in the middle and strands the
       italic "The" on the roman line, so the two type styles read as one
       run-on. The second is the same break the desktop has.

       It is safe to show because BOTH clauses fit a 330px phone column on
       their own at 25.6px — 250px and 215px, measured. That is not true of
       the other <br> headings on the page, which is why the blanket rule
       above stays and this is an exception rather than a deletion.

       ⚠️ THE SPACE BEFORE THE <br> IS STILL LOAD-BEARING. The markup reads
       "Compounds. <br><em>The" — if that space goes, the hidden-<br> case
       elsewhere renders "Compounds.The". Do not tidy it. */
    #hv4-cost h2 br {
        display: inline
    }

    #hv4-hero h1,
    .hv4 h2,
    .hv4 .hv4-aside-title {
        text-wrap: pretty
    }

    /* *** "REPEAT" GETS ITS OWN LINE, AND THAT MEANS GREEDY WRAPPING. ***
       10-01: "let's just put repeat on its own line here", confirming the
       earlier "fill the first line" choice on the same heading.

       "One Slice, Ninety Days, Repeat" is 349px against a 330px column — it
       misses fitting one line by 6%. Both `balance` and `pretty` answer that
       by splitting it evenly, 197/145, which puts "Days, Repeat" on line two
       and reads as an arbitrary break in the middle of the list. `wrap` is
       plain greedy fill: 264/78, so the first two items stay together and the
       third stands alone — which is what the headline means, since "Repeat"
       is the move the first two produce.

       This is the one heading on the page where the orphan is WANTED, so it
       is deliberately exempt from the `pretty` rule above and from the
       standing no-orphans instruction. Do not "fix" it back. */
    #hv4-delivery h2 {
        text-wrap: wrap
    }

    /* *** h3 CAME OUT OF THE LIST ABOVE ON 10-01. *** Everything in that note is
       about h2-sized headings on a 330px column — strings long enough to greedy-
       wrap three or four times, where `balance` equalises them into a narrow
       block. The h3s are not those. They are short, they wrap to two lines, and
       `pretty` only intervenes when the LAST line is short, so on a two-line
       heading it does nothing at all and the greedy fill stands. Measured at 375:

         The Need for Agentic Governance   pretty -> "The Need for Agentic" /
                                           "Governance"  — a one-word last line
                                           balance -> "The Need for" / "Agentic
                                           Governance"

       Probed all five h3s under both values: `balance` fixes two and is never
       worse on the other three. The standing "no orphans" rule is the reason the
       list uses `pretty` in the first place, and on these headings `pretty` is
       what PRODUCES the orphan — same instruction, opposite value, because the
       line count is different. This is also just styles.css:53-57's site-wide
       value, so h3 could have been dropped to inherit it; it is written out
       because the difference from the rule above is the point. */
    .hv4 h3 {
        text-wrap: balance
    }
}

/* *** THE TWO HEADLINE SIZES BELOW ARE THE SITE STANDARD, VERBATIM. *** .hv4 h2
   at clamp(1.6rem, 6.8vw, 2.3rem) and .hv4 .hv4-aside-title at
   clamp(1.25rem, 5.5vw, 1.6rem) are copied from styles.css:1214, 1231, 1401,
   1597, 1816, 1931. Do not retune them here. liminalarc.co is the standard and
   this page is supposed to match it.

   *** BOTH WERE DELETED ON 10-01 AND RESTORED THE SAME DAY. *** They were
   replaced by calc(rem + vw) ramps on the base rules that ran unbroken from
   phone to desktop. The ramps did what they were built to do at the two ends,
   and raised every section headline in the MIDDLE as a side effect — 28.8 ->
   35.0 at 992, 29.7 -> 35.4 at 1024. Jess, on the laptop screenshot: "put the
   jump back." The full measurement and the arithmetic reason (a line with a
   positive intercept sits above an origin line everywhere below their shared
   point) are on the .hv4 h2 base rule. DO NOT REBUILD THE RAMP.

   ⚠️ THE 767/768 CLIFF IS REAL AND IT IS INHERITED, NOT A BUG ON THIS PAGE.
   These clamps and the base ones are both bare-vw, so they are lines through
   the origin with different slopes and can only meet at zero. At the boundary
   .hv4 h2 goes 36.8 -> 24 and #hv4-hero h1 goes 43.2 -> 32: the headline gets
   SMALLER as the window gets WIDER. styles.css has the same cliff on every
   page. It was offered as a fix and declined in favour of matching the rest of
   the site. If styles.css is ever fixed site-wide, fix it there, not here.

   ⚠️ THE HERO IS NOT IN THIS BLOCK AND THAT IS NOT AN OMISSION. Its phone size
   lives in the ≤991 block with its tablet size, because "raise the hero" was
   one decision covering both bands. It is the one headline on this page that is
   NOT the standard's value. See that rule.

   ⚠️ READ THE BREAKPOINT, NOT JUST THE VALUE. These two rules sat in the ≤991
   block until 09-30 — one rung too early, every number correct, the comment
   above them already saying "≤767". Cost: at 768 the hero measured 32px while
   #hv4-cost h2 measured 36.8px, so the section headline outsized the page
   headline for the whole 768–991 band. A clamp filed under the wrong @media
   line looks completely correct in the source. The mechanism is the vw term:
   6.8vw is sized for a phone, and at 768 it computes 52px and pins to its own
   2.3rem ceiling while the hero is still on its desktop floor. Check any
   type-scale change at the TOP of its band as well as the bottom.

   The body copy below steps at 767 the standard's way too — flat rem values. */
@media(max-width: 767px) {
    .hv4 h2 {
        font-size: clamp(1.6rem, 6.8vw, 2.3rem)
    }

    /* ITALIC ON PHONES ONLY. "maybe on mobile we make these two titles italic",
       10-01, pointing at "Capabilities Are the Unit of Decision".

       It works here and would not work at desktop, and the reason is the size
       ladder in the note above. At 1440 the three headline sizes read 56.2 /
       41.8 / 32.0 — the aside title is a clear 10px step below the section h2,
       so it is already visibly a smaller kind of heading. At 375 they read
       36.0 / 25.6 / 20.6 and the bottom two steps are only 5px apart on a 330px
       column, which is not much separation for the one block that is a titled
       aside rather than a section. The slope does that work on a phone without
       spending any more size.

       ⚠️ IT IS NOT THE ASIDE'S BODY COPY COMING BACK. That was italic, inherited
       from .pa-bio-quote, and it was taken OUT on 09-17 ("let's try this not
       italic") because a pull quote is italic for being someone else's words and
       these paragraphs are the page speaking. A one-line title is a different
       object from four lines of prose; this does not reopen that. */
    .hv4 .hv4-aside-title {
        font-size: clamp(1.25rem, 5.5vw, 1.6rem);
        font-style: italic
    }

    /* ⚠️ `.hv4-aside p.hv4-lead` is (0,2,1) and this block was (0,1,0), so the
       aside kept its DESKTOP size on phones — 18px body copy under a 20.6px
       title, while the section h2 below it ran 25.6px over a 16px lead. The
       aside stopped reading as one size down from the section and started
       reading as a bigger paragraph with a small heading on it. A media query
       does not beat specificity; the selector has to be repeated in full. */
    /* .hv4-order-close ADDED 10-01, the phone half of the same broken pair.
       It was absent here for the same reason it kept the old clamp at
       desktop: the rule copies .hv4-aside p.hv4-lead and only one of the two
       ever gets updated. On a phone its clamp bottomed out at 1.125rem = 18px
       against a 16px lead — the same inversion as desktop, 2px instead of
       2.4px. Both halves are now in both places. */
    .hv4-lead,
    .hv4-aside p.hv4-lead,
    .hv4-order-close,
    .hv4-body p {
        font-size: 1rem
    }
}
