/* ============================================================
   Capital Mortgages — homepage styles
   ============================================================ */

/* ---------- hero ---------- */
/* The header is a flat opaque bar now (anthropic-style), so the hero simply
   starts below it — no pull-under, no compensating padding.
   The photograph is the hero's GROUND at every size now — full-bleed behind
   the copy, edge to edge — not a band below it. The copy sits on a warm
   paper wash (the scrim below) so the ink type keeps its contrast, and the
   picture emerges clean on the right and in the band under the text. */
.hero {
  padding: 44px 0 0; position: relative; overflow: hidden;
  /* The copy column's width, and the ONE number the scrim's ramp is anchored
     to — see .hero-media::after. They have to move together: the ramp's whole
     job is to go solid until the text ends and then get out of the way, so a
     column that changes width at a breakpoint without the ramp following it
     puts the type on the feather. It steps down twice because the column is a
     fixed measure and the frame is not: left at 620px it would be taking 73%
     of a 900px window and there would be no photograph left to see.
     The gutter is NOT redeclared here any more. It used to be shadowed with
     a local 36px/22px pair mirroring .wrap's fixed padding; .wrap's gutter is
     the fluid --gutter token now (see shared.css), so the hero inherits the
     real value and the scrim's geometry tracks it automatically at every
     width. Shadowing it would silently desync the ramp from the column. */
  --hero-col: 540px;
  /* The frame's height. The stats band lives INSIDE this section now (the
     hero is a flex column and the band is its last child), so the height is
     stated once and read once: header + hero-including-band = 100svh
     EXACTLY. The old fold choreography — the hero's foot landing on the fold
     so the band opened the second screen whole — existed only because the
     band was the section AFTER the hero and must never straddle the fold.
     With the band as the frame's own foot there is nothing to straddle: the
     fold lands on the band's bottom rule and the whole landing composition
     (copy, picture, figures) is the first screen. svh, not vh: on phones vh
     is the LARGEST viewport, so 100vh - bar overshoots the visible frame by
     the browser chrome. */
  /* Trimmed when the band moved in: the frame now pays for ~175px of stats
     out of the same 100svh, and the paddings, the copy's row-gap and the
     headline's vh cap are where it comes from — see .hero h1's font-size
     note. At 1440x900 the whole composition fits the frame with ~8px of
     slack; on shorter windows the band degrades to partially below the
     fold, which is still strictly better than the second-screen band it
     replaced. */
  --hero-pad-top: clamp(44px, 5.4vh, 76px);
  --hero-pad-bottom: clamp(20px, 2.6vh, 40px);
  /* - 1px: the header is --bar-h plus its 1px bottom border (transparent at
     the top of the page, but it still takes layout height). */
  --hero-min: max(460px, calc(100svh - var(--bar-h) - 1px));
}
/* On very tall viewports the frame stops growing: 1120 ≈ the old 940px hero
   cap plus the ~180px band it now contains. 1210 = bar (85) + 1120 + a few
   px — the first height at which the cap leaves the frame clear of the
   fold rather than cut by it. */
@media (min-height: 1210px) { .hero { --hero-min: 1120px; } }
@media (max-width: 1080px) { .hero { padding-top: 34px; --hero-col: 490px; } }
@media (max-width: 900px)  { .hero { --hero-col: 440px; } }
@media (max-width: 480px)  { .hero { padding-top: 26px; } }
/* The figure's containing block is the GRID now, not the section: the stats
   band is the section's last child (see .hero.darkbg further down) and the
   photograph must end where the band begins, so the picture's box is the
   grid's box — stretched back out to the section's edges below. */
.hero .hero-grid { position: relative; }
.hero-copy { position: relative; z-index: 1; }

/* --- the photograph, as the section's full-bleed backdrop ---
   No border, no radius, no margins: it is the ground the hero stands on,
   not an object on the page. The section's own height (copy + padding)
   sizes it; min-height keeps the picture present on short-copy wide
   screens. No entrance animation either — the panel's 18px rise read as
   an arrival when the photo was an object, but the whole ground sliding
   up under the type reads as a glitch. The img stays free of opacity
   animation regardless: it is the LCP element and must paint at frame 0. */
.hero-media {
  position: absolute; z-index: 0;
  /* The box: from the section's TOP edge (the negative top backs out of the
     section's own padding, which sits above the grid) down to the grid's
     foot — which is the top of the stats band, the one edge inset:0 on the
     old section-scoped figure could not find. Horizontally the grid is the
     centred .wrap, so the figure walks back out to the viewport with the
     50%−50vw full-bleed offset; with a classic scrollbar 100vw overshoots
     the layout width by the bar, but the overshoot splits across both edges
     and the section's overflow:hidden clips it, so the print always covers.
     The scrim's --copy-edge math below still holds: its percentages now
     resolve against this 100vw box instead of the section's, a drift of at
     most half a scrollbar — inside the ramp's near-solid tolerance. */
  top: calc(-1 * var(--hero-pad-top)); bottom: 0;
  left: calc(50% - 50vw); width: 100vw;
  margin: 0;
  overflow: hidden;
}
/* <picture> is an inline box by default, so it would collapse to the line box
   and leave the fitted image unsized — it has to carry the panel's dimensions
   through to the img. */
.hero-media picture { display: block; width: 100%; height: 100%; }
/* The paper the photograph is printed ON. It has to be a real painted colour
   here, not inherited from the page: .hero-media is position:absolute with
   z-index:0, which makes it a stacking context, so the img's multiply below
   blends against THIS box and never reaches the body background. Without it
   the blend has a transparent backdrop and silently does nothing. */
.hero-media { background: var(--bg); }
.hero-media img {
  display: block; width: 100%; height: 100%;
  /* No scale any more. The print is composed AS the hero band now — the
     impression runs edge to edge — so cover fits it to the frame and every
     pixel of the master is in play. The 1.78x that used to live here was
     cropping past the old sheet's wide paper margins, and it was also the
     reason the hero looked soft: it magnified about a third of a ~1MP
     generation across the full width of the screen.

     The Y offset is all that is left: the frame is wider than 16:9, so cover
     trims top and bottom, and 58% holds the rooflines and the shopfronts in
     preference to sky. */
  object-fit: cover;
  object-position: 50% 58%;
  /* Ink on paper. multiply lets the page's own ground show THROUGH the print
     instead of the print sitting on top of it, which is what ties the sheet
     and the page into one surface.

     There is no brightness here any more, and it must not come back. The
     generated sheet was cream, and a filter was the only way to key that
     cream off in the browser — at the cost of washing the inks with it,
     since filter runs BEFORE the blend and lifts every channel. The white
     point is corrected in the ASSET now (assets/_originals holds the
     corrected master), so the paper arrives white, multiply drops it into
     the page cleanly, and the inks keep their full density.

     Note for future edits: mix-blend-mode was stripped out of this codebase
     once before because the headless preview compositor white-outs sections
     that use it. That is a PREVIEW artefact — real browsers render it — but
     it means screenshots of this hero cannot always be trusted. */
  mix-blend-mode: multiply;
}
/* --- the scrim: a warm paper wash the copy sits on --------------------------
   SOLID page paper over the copy column, a short feather, then nothing at
   all — the photograph runs at full strength across the right of the frame.

   The previous ramp held 96% paper to 42% of the width and only reached
   transparent at 86%, with a second wash climbing 34% up from the bottom.
   Every pixel of the picture was under something, so the whole frame read
   hazy and the building never actually arrived. What makes a composition
   like this work is the CONTRAST between a clean paper column and a clean
   photograph, not a gradient between them; the feather is now the only mixed
   zone, and it is ~150px wide, which is what the reference's is.

   The stops are anchored in the COPY COLUMN's real right edge rather than in
   percentages of the frame. Percentages drift: the wrap is centred, so as
   the window grows past --maxw the column's right edge walks from 40% of the
   width toward 50%, and a fixed 46% stop that cleared the text on a laptop
   would have the text standing on the feather on a wide monitor.
   --copy-edge computes it directly — half the frame, back out to the wrap's
   left edge, then in by the gutter and the column's own width — and min()
   makes the same expression correct below --maxw, where the wrap is simply
   the frame. (Verified computing to a real gradient: Chromium rejects calc()
   in a radial-gradient's RADIUS, which silently voids the whole
   declaration, but linear-gradient colour-stop POSITIONS take it.)

   Built from var(--bg) with color-mix so every tone preset (warm paper,
   white fallback) makes its own paper. */
.hero-media::after {
  content: ""; position: absolute; inset: 0;
  /* 45px INSIDE the copy column's right edge, not at it. The column is a
     measure, not a hard edge — the longest headline line stops short of it —
     so holding solid paper all the way to 540px was spending frame on
     nothing. Pulling the ramp back in gives the picture that width instead.
     It was 117px, which took back more than the measure could spare. At
     1600px the headline's longest line reaches 695px and the dek's block
     ends at 704; 117px put the end of the solid paper at 587, so the last
     ~108px of that headline was standing on the feather. 45px moves it out
     to 659 — the tail of the longest line now sits in the first 36px of the
     ramp, where the wash is still near-solid — and still hands the picture
     the slack the column was not using. */
  --copy-edge: calc(50% - min(var(--maxw), 100%) / 2 + var(--gutter) + var(--hero-col) - 45px);
  background:
    linear-gradient(to right,
      color-mix(in oklab, var(--bg), transparent 9%) 0,
      color-mix(in oklab, var(--bg), transparent 9%) var(--copy-edge),
      color-mix(in oklab, var(--bg), transparent 40%) calc(var(--copy-edge) + 68px),
      color-mix(in oklab, var(--bg), transparent 82%) calc(var(--copy-edge) + 116px),
      transparent calc(var(--copy-edge) + 152px));
}
body[data-herobg="off"] .hero-media { display: none; }
/* The photo takes the LARGER share. At 7/5 it was 435px against 610px of
   copy and read as a sidebar illustration rather than a partner — the two
   columns have to be near-equal before they pair, and giving the picture
   the edge is what makes it the thing you look at first. The headline pays
   for it by dropping from 64px to 54px, which it can afford: it still holds
   two lines in the narrower column. */
/* 1.3, up from 1.14. The panel is bottom-capped at the credential strip now
   rather than running past it, which cost it height — so it takes the width
   back. Narrowing the copy also pushes the headline to three lines, which
   makes the column taller, which the panel inherits: the two moves pull in
   the same direction and the picture ends up the size it was. */
/* One column: the statement, then the band. The copy holds a reading
   measure; the photograph escapes the wrap on its own negative margins. */
.hero-grid { display: block; }
/* ~34% of a 1440 frame, which is where the reference's copy column sits. In
   ch it tracks the body serif, so it is stated in px against the headline
   instead — that is the run that decides the wrap. */
.hero-copy { max-width: var(--hero-col); }
/* --- the headline ----------------------------------------------------------
   Fraunces, and MUCH bigger. The reference sets its headline at about 4.7%
   of the frame width and lets it take three lines; this was General Sans at
   54px, which on a 1440 laptop is 3.7% and two lines — a headline sitting IN
   the composition rather than being it. clamp() runs it from 40px on a phone
   to 92px on a wide desktop, tracking the viewport in between so the line
   count stays put as the window moves.

   line-height comes down to .98 with the size: at 90px the old 1.08 opened
   ~7px more between the lines than the reference has, and a display serif
   wants to stack tighter than a grotesque. The negative tracking is
   Fraunces' own — it sets loose at display sizes.

   font-optical-sizing:auto is the point of using a variable serif here: at
   this size Fraunces picks its display cut, which has the stroke contrast
   the reference's face does. Without it the headline renders in the text cut
   and reads flat and a little wide. */
.hero h1 {
  font-family: 'Fraunces', var(--serif);
  font-optical-sizing: auto;
  font-weight: 550;
  /* 6.1vw — 88px on a 1440 laptop, back down ~15% from the 104px it briefly
     ran at. Two things were wrong at that size and only one of them was the
     size: the block pushed the intent band onto the fold line, where a
     900px-tall viewport technically contained it and no real browser did
     (chrome eats 80-120px), and four lines of a high-contrast serif at 104px
     is more voice than a mortgage brokerage wants in its first sentence.

     The WEIGHT comes down with it, 600 → 550, which is most of what makes it
     read calmer. Stroke weight is what "overbearing" usually means at
     display sizes — a headline can be large and quiet, but not heavy and
     quiet. Fraunces is variable across 300–900, so this costs nothing.

     line-height is 1.04, up from .95, and that is a BUG FIX rather than a
     taste change. Measured on the canvas at the rendered size, Fraunces'
     ink extent — tallest ascender to deepest descender — is 86.7px at an
     87.84px font-size, so the true minimum before adjacent lines touch is
     0.987. At .95 the line box was 83.4px: a 3.2px overlap, which is
     exactly the descender of the "g" in "mortgage" landing on the "d" and
     "f" ascenders of "found," on the line below.

     1.04 leaves ~6.5px of clearance at this size and still reads tight for a
     display serif. Anything below 0.99 will collide again — and it will only
     show on the glyph pairs that happen to line up, which is why it survived
     several passes unnoticed. */
  /* Sized by the SMALLER of the two constraints. A hero headline set from
     width alone ignores the axis it actually competes for: on a 1366x768
     laptop the frame is wide enough to ask for 88px, and the 635px of
     viewport left under the header cannot pay for it — the intent band ended
     up 36px under the fold on exactly the machines most likely to be looking.
     The vh cap was 10vh when the copy had the whole frame under the header;
     the stats band lives inside the frame now (see .hero.darkbg) and takes
     ~90px of it, so the cap tightened to 9.8vh — which a 900px window
     never reaches (6.1vw governs from ~1290px wide), so on a standard
     laptop the headline is back at its designed 88px and copy + band still
     fit 100svh with ~40px of slack. Same reasoning as the 104px→88px step
     before it: the headline pays for the fold, because nothing else in the
     column has this much height to give. (It ran at 8vh when the band was
     173px tall and 9.4vh at 125px; each slimming of the band bought size
     back.)

     vh is safe here in a way it usually is not: on a phone 6.1vw is ~24px, so
     the 40px floor governs and a moving address bar cannot resize the type. */
  font-size: calc(clamp(40px, min(6.1vw, 9.8vh), 100px) * var(--h1k, 1));
  line-height: 1.04;
  letter-spacing: -0.024em;
}
/* --- the emphasised word ---------------------------------------------------
   The accent CHANGES VOICE rather than getting decorated: Source Serif at
   brand red inside a General Sans headline. It was a thick red bar drawn
   under the word (2026-08-09), which works, but a rule is a mark ON the type
   — a serif in the middle of a grotesque is a different SPEAKER, and it is
   the move the editorial heroes in this class actually make. It also frees
   the underline to mean one thing on the page again (the secondary link
   below reads as a link because it is underlined).

   Source Serif has no italic on disk and the site's italic display face was
   retired with the anthropic-style pairing, so this stays upright: a
   browser-synthesised oblique at 54px shears the terminals and looks like a
   rendering fault. The serif runs about 4% small against General Sans at the
   same size — its caps are shorter — so it is scaled back up to match, and
   the tighter tracking keeps it from reading loose beside the grotesque. */
.hero h1 em {
  font-family: 'Fraunces', var(--serif);
  font-style: italic;
  /* a hair heavier than the roman around it: Fraunces' italic reads lighter
     than its upright at the same weight, so matching the NUMBER would leave
     the accent looking thin next to the words it interrupts */
  font-weight: 580;
  color: var(--red);
  text-decoration: none;
}
/* The copy column is a plain vertical stack now. It used to be a 12-column
   grid that split itself into a claim on the left seven and a statement on
   the right five — that split existed because the photograph ran the full
   width behind it and the statement had to sit somewhere the type could be
   defended. With the photo in its own panel the whole column belongs to the
   copy, in document order, and the word-reveal in site.js still walks
   .hero-copy's direct children exactly as before. */
.hero-copy {
  /* Flex, not grid. The reference distributes this column: the copy block
     packs at the top and the logo band sits on the frame's bottom edge, with
     the slack between them. A grid with align-content:start gives every row
     its content height and leaves no free space for `margin-top:auto` to
     take, so the band could only ever hang directly off the strip above it.
     Nothing else here depends on it being a grid — site.js walks
     .hero-copy's direct children either way. */
  display: flex;
  flex-direction: column;
  /* Natural flow, top-aligned. Pushing the credential strip down onto the
     photo's bottom edge did align the two columns, but it opened 136px
     between the buttons and the strip and the strip read as floating —
     the opposite of belonging to the block above it. The strip stays where
     it belongs and the photograph is simply the taller column. */
  align-items: flex-start;
  row-gap: clamp(14px, min(1.8vw, 2.1vh), 26px);
  /* Natural height — no frame-mirroring min-height any more. The column used
     to be held to the frame so `margin-top:auto` on the intent band could
     drop it to the foot of the picture; the band sits with the copy now and
     the STATS band takes the frame's floor instead (see .hero.darkbg), so
     the frame's slack lands below the column as clear photograph. */
  /* One gap everywhere read as a list, not a composition. The kicker is the
     headline's label — half the standard gap ties them into a single unit,
     and that one tightened joint is what makes the equal gaps below it read
     as deliberate rather than mechanical. (Negative margin rather than a
     second row-gap: a grid has one row-gap, and this is the one exception.) */
  /* The blocks below the headline fill the column. They were on 40ch and
     44ch, which left the headline reaching 146px further right than either
     of them — three right edges in a column standing next to a photograph
     with one. Filling the track gives the left side a single edge to answer
     the picture with, which is most of what makes the two halves pair. */
  --hero-measure: 100%;
}
/* Half the standard gap between the kicker and the headline — the kicker is
   the headline's label, and that one tightened joint is what makes the equal
   gaps below it read as deliberate rather than mechanical. It has to stay
   exactly half of .hero-copy's row-gap: negative margin rather than a second
   row-gap, because a flex column has one gap and this is the one exception. */
.hero-copy > h1 { margin-top: calc(clamp(14px, min(1.8vw, 2.1vh), 26px) / -2); }
/* The statement is a serif dek now. When the hero was all-grotesque the
   sans-500 statement was the second voice; with the headline in Source
   Serif the sans line became the foreign object — a UI voice interrupting
   an editorial opening. Serif 400 under a serif 600 headline is the
   newspaper dek pattern: same family, one weight step down, clearly prose
   rather than interface. Sans 500 remains the SUPPORT voice for section
   ledes elsewhere; the hero simply belongs to the reading voice now. */
/* The dek is SANS again. It was Source Serif 400 — the newspaper dek pattern,
   same family as the headline, one weight step down — which was right while
   the headline was a grotesque and the serif was the second voice. With the
   headline itself a display serif that reasoning inverts: two serifs stacked
   read as one undifferentiated block, and the reference sets its paragraph
   in a grotesque against a serif headline for exactly that contrast. */
.hero-sub {
  font-family: var(--sans);
  /* one step above the 19px body, the way anthropic.com runs 24px lead
     paragraphs over a 20px base */
  font-size: 21px; font-weight: 400; line-height: 1.55;
  letter-spacing: -0.003em;
  color: var(--ink-soft);
}

/* --- the dek ---------------------------------------------------------------
   No brackets. It was framed by hairlines above and below (the remains of an
   outlined box), which set the paragraph apart as a pull-quote — the
   reference just runs it as body copy under the headline, and with the
   headline now carrying this much weight the paragraph does not need help
   being distinguished from it. The rules were also the only two horizontal
   lines in the upper half of the column, and they cut the copy block into
   three pieces where the reference reads as one.

   For the brackets back: border-top/border-bottom of 1px var(--line) plus
   the vertical padding this rule drops. */
.hero .hero-sub {
  width: 100%;
  max-width: 46ch;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
/* The statement's red dash is gone. It was "the same gesture as the
   eyebrow's em-dash" back when the two runs sat across the hero from each
   other; stacked in one column, and with the headline's drawn rule now
   carrying brand red 30px above it, it had become a second red mark with no
   job. The brackets alone set the block off. */
/* align-items:center so the bare link lines up on the button's centre rather
   than its text baseline — the two have different box heights now. The tighter
   gap is scoped to the hero: the link carries 16px of its own side padding, so
   the standard 12px between two chips would read as 28px here. The closing
   panel still pairs two real buttons and keeps the 12. */
.hero-ctas { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.hero .hero-ctas { gap: 4px; }

/* --- the secondary action, as a link ---------------------------------------
   A bare underlined run of text with an arrow, not a second chip. The ghost
   button beside a filled one gave the hero two boxes of near-equal weight,
   which is a fork in the road rather than a primary and a way out; it also
   sat as a pale slab over the photograph's feather. Padding matches the
   button's vertical padding so the two share a centre line, and the
   underline lives on the inner span so the arrow stays clear of it — the
   arrow is a direction, not part of the word. */
.hero-link {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--sans); font-size: 15.5px; font-weight: 540;
  line-height: 1;
  padding: 15px 16px;
  color: var(--ink);
  white-space: nowrap;
  transition: color .18s ease;
}
.hero-link > span:first-child {
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: .38em;
  text-decoration-color: color-mix(in oklab, var(--ink), transparent 62%);
  transition: text-decoration-color .18s ease;
}
.hero-link:hover { color: var(--red); }
.hero-link:hover > span:first-child { text-decoration-color: currentColor; }
.hero-link .arrow { transition: transform .18s ease; }
.hero-link:hover .arrow { transform: translateX(3px); }

/* --- the credential line ---------------------------------------------------
   ONE line, not a table and not two columns. As four stacked rows with a rule
   under each it was 189px tall — taller than the dek above it — and read as a
   data table parked under the buttons. The trust signal under a hero CTA is a
   single horizontal band, and licensing text is kept deliberately small.

   The pairs are CONTENT-SIZED, not equal thirds. This was
   `grid-template-columns: repeat(2, minmax(0,1fr))`, which forced both cells
   to half the column: "Accreditation BBB" is ~157px of content in a 270px
   track, so the divider stood 113px clear of the word it followed and the two
   facts read as separate columns with a hole between them rather than as one
   line. Equal tracks are for things that should LOOK equal; these are two
   label/value pairs of different lengths, and proximity — each value sitting
   nearer its own label than the next pair — is what makes the row parse.

   The divider is a border-left on the second pair, so a line-break would
   leave it stranded in mid-air. nowrap plus an explicit stacking breakpoint
   below, rather than flex-wrap, is what keeps that from ever happening.

   It briefly carried the BBB and Mortgage Centre MARKS instead of these two
   labels, on the reasoning that a reader scanning a hero recognises a badge
   faster than it reads a word. Both ship as white-background PNGs, though, so
   on warm paper they read as pasted rectangles and need a chip to look
   deliberate — and they already run in the footer, on exactly that chip. Two
   small white boxes in the middle of the opening frame cost more than the
   recognition was worth. */
.hero-proof {
  margin: 0;
  width: 100%;
  max-width: var(--hero-measure);
  display: flex;
  align-items: baseline;
  flex-wrap: nowrap;
  gap: 28px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}
/* baseline on the PAIR, so the label and value sit on one line together; the
   wrapper itself has to align to the row's baseline, not its box top */
.hero-proof > div { display: flex; align-items: baseline; gap: 9px; }
.hero-proof > div + div { padding-left: 28px; border-left: 1px solid var(--line); }
.hero-proof dt {
  font-family: var(--sans);
  font-size: 11.5px; font-weight: 640;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-faint);
  white-space: nowrap;
}
.hero-proof dd {
  margin: 0;
  font-family: var(--sans);
  font-size: 15px; font-weight: 600;
  color: var(--ink);
  font-variant-numeric: lining-nums tabular-nums;
  /* a wrapped value turns the strip into two lines and leaves the divider
     standing taller than the text beside it */
  white-space: nowrap;
}
/* Phones keep the two pairs on ONE line, which is the whole point of the row —
   stacked, they stop being a credential strip and become two more lines of
   copy. The pairs measure ~273px of ink together, so what has to give is the
   space around the divider (28px each side is desktop breathing room, not a
   requirement) and, further down, the label tracking: .14em over
   "ACCREDITATION" and "FSRA LICENCE" alone is ~14px of the width. At 375px
   that lands the row at ~299px inside 331px of column.

   Below 360px the ink itself no longer fits and the only remaining lever
   would be shrinking the smallest type on the page, which is not a trade
   worth making — so there, and only there, they stack and the divider goes,
   since a rule means nothing between things that are not side by side. */
@media (max-width: 620px) {
  .hero-proof { gap: 16px; }
  .hero-proof > div + div { padding-left: 16px; }
}
/* At 480px and under the row still runs ~7px past the column, and the space
   around the divider has nothing left to give. What does have slack is the
   LABEL: "ACCREDITATION" and "FSRA LICENCE" together are ~213px of a 331px
   column to say what their values already imply, and they are the row's
   secondary voice, not its content. A step down to 10.5px with tighter
   tracking returns ~24px and keeps the value at a full 15px — so the
   hierarchy actually SHARPENS at the size where it matters most, instead of
   both runs shrinking together. */
@media (max-width: 480px) {
  .hero-proof { gap: 12px; }
  .hero-proof > div + div { padding-left: 12px; }
  .hero-proof > div { gap: 8px; }
  .hero-proof dt { font-size: 10.5px; letter-spacing: .08em; }
}
@media (max-width: 359px) {
  .hero-proof { flex-direction: column; gap: 9px; }
  .hero-proof > div + div { padding-left: 0; border-left: 0; }
}

/* --- the intent band -------------------------------------------------------
   The four things a visitor might be here for. It replaced a row of lender
   marks, which looked right and said nothing: the full lender wall is one
   section further down, so five of them up here were the same claim twice
   inside one scroll.

   It sits WITH the copy now, one breath below the credential strip — the
   old `margin-top:auto` dropped it to the foot of the column, which read as
   part of the composition on a wide desktop and as a stranded row on
   everything narrower. The frame's floor belongs to the stats band (see
   .hero.darkbg); this row is content, so it stays with the content. The
   padding is only a touch over the column's own row-gap: enough to read as
   its own small band, not enough to detach it. No hairline above it: the
   gap is the separator, and a rule there would read as a second credential
   strip. */
.hero-intent { padding-top: clamp(8px, 1.4vh, 16px); }
.hero-intent-label {
  font-family: var(--sans);
  font-size: 11.5px; font-weight: 640;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-faint);
}
.hero-intent-row {
  list-style: none; margin: 13px 0 0; padding: 0;
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 8px 16px;
}
.hero-intent-row > li + li { padding-left: 16px; border-left: 1px solid var(--line); }
.hero-intent-row a {
  font-family: var(--sans);
  font-size: 15px; font-weight: 540;
  color: var(--ink-soft);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .32em;
  text-decoration-color: transparent;
  transition: color .18s ease, text-decoration-color .18s ease;
}
.hero-intent-row a:hover { color: var(--red); text-decoration-color: currentColor; }
/* Wrapping is allowed here, so a stranded border-left at the start of a new
   line is the failure to avoid — below this the dividers go and the row falls
   back to plain gaps.

   The links also grow a touch target. At 15px with no padding the hit area is
   the ~20px of the text itself, which is under half the 44px minimum and the
   kind of thing that only shows up on a real thumb. Negative margin keeps the
   padding from opening the row's visual rhythm — the boxes overlap into the
   gap, the ink does not move. */
@media (max-width: 620px) {
  .hero-intent-row { gap: 4px 14px; }
  .hero-intent-row > li + li { padding-left: 0; border-left: 0; }
  .hero-intent-row a { display: inline-block; padding: 12px 0; margin: -12px 0; }
}

/* editorial layout: the outer grid is the copy/photo split, so this variant
   only needs to place the (now unused) logo and rate-card slots after it */
body[data-hero="editorial"] .hero-grid { align-items: stretch; }
body[data-hero="editorial"] .hero-copy { grid-column: 1; grid-row: 1; }
body[data-hero="editorial"] .hero-logo { grid-column: 2; grid-row: 1; }
body[data-hero="editorial"] .hero-side { grid-column: 1 / -1; grid-row: 2; }
/* no max-width: the column is the measure now, and a 15ch cap on top of it
   just wrapped the headline earlier than the track needed */
/* the size lives on .hero h1 now (one clamp, one place); this only has to
   undo the 15ch cap the preset used to put on it */
body[data-hero="editorial"] .hero h1 { max-width: none; }
body[data-hero="editorial"] .hero-photo { display: none; }
body[data-hero="editorial"] .rates-card { width: 100%; }
body[data-hero="editorial"] .rates-rows { grid-template-columns: repeat(3, 1fr); }

/* big brand logo, nicely displayed in the hero's right column */
.hero-logo { display: grid; place-items: center; }
.hero-logo img {
  width: 100%; max-width: 392px; height: auto; display: block;
  filter:
    drop-shadow(0 26px 54px rgba(176, 18, 28, 0.16))
    drop-shadow(0 10px 22px rgba(29, 27, 23, 0.10));
}
body[data-hero="split"] .hero-photo { aspect-ratio: 5 / 4; }
body[data-hero="split"] .hero-side { display: grid; gap: 24px; }

body[data-rates="off"] .rates-card { display: none; }

/* rates card */
.rates-card { padding: 26px 28px 24px; }
.rates-head { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; margin-bottom: 18px; }
.rates-head .eyebrow { color: var(--ink-faint); }
.rates-updated { font-size: 12.5px; color: var(--ink-faint); }
.rates-rows { display: grid; gap: 0; }
.rate { display: grid; gap: 2px; padding: 14px 0; border-top: 1px solid var(--line-soft); }
body[data-hero="editorial"] .rate { border-top: 0; border-left: 1px solid var(--line-soft); padding: 4px 0 4px 22px; }
body[data-hero="editorial"] .rate:first-child { border-left: 0; padding-left: 0; }
.rate .term { font-size: 13.5px; font-weight: 560; color: var(--ink-soft); }
.rate .value { font-family: var(--serif); font-size: 34px; font-weight: 560; letter-spacing: -0.02em; line-height: 1.1; font-variant-numeric: lining-nums tabular-nums; }
.rate .value small { font-size: 18px; }
.rate .note { font-size: 12.5px; color: var(--ink-faint); }
.rates-foot { margin-top: 16px; font-size: 13px; color: var(--ink-soft); }
.rates-foot a { color: var(--accent); font-weight: 600; }
.rates-foot a:hover { text-decoration: underline; text-underline-offset: 3px; }


/* ---------- stats band ---------- */
.stats { margin-top: 88px; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--bg-deep); }
.stats .wrap { display: grid; grid-template-columns: repeat(4, 1fr); }
/* A strip, not a block: the band lives inside the landing frame now, and
   every px of its height is a px taken from the copy and the picture, so it
   runs at roughly two-thirds of its old chrome — 16px verticals against the
   old 30, the figures a step down, the labels snugged. */
.stat { padding: 10px 28px; border-left: 1px solid var(--line); display: grid; gap: 2px; }
.stat:first-child { border-left: 0; padding-left: 0; }
.stat .n { font-family: var(--serif); font-size: 26px; font-weight: 580; letter-spacing: -0.02em; font-variant-numeric: lining-nums tabular-nums; }
.stat .l { font-size: 14px; font-weight: 540; color: var(--ink-soft); line-height: 1.35; }

/* ---------- flat bordered cards (anthropic card language) ----------
   The lower homepage swaps liquid glass for the reference's flat surface +
   hairline border; hover darkens the border and lifts a touch. */
/* body-prefixed to outrank the tone-variant .card shadow in shared.css */
/* the quote markup still carries .card; strip the panel treatment off it */
body .quote.card {
  background: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
body .renew-strip.card, body #calculator .cresult.card {
  background: var(--surface);
  -webkit-backdrop-filter: none; backdrop-filter: none;
  border: 1px solid var(--line);
  box-shadow: none;
}

/* ---------- services — anthropic's filled-panel card ----------
   Their cards are a solid tinted panel sitting on the page ground: no
   hairline, no shadow, no lift. The surface deepens one step on hover and
   the arrow walks forward — that restraint is what stops a card reading as
   a generic template. */
.svc-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
body .svc.card {
  background: var(--bg-deep);
  border: 0;
  border-radius: 18px;
  box-shadow: none;
}
.svc {
  padding: 34px 32px 30px;
  display: grid; gap: 12px; align-content: start;
  transition: background .22s ease;
}
body .svc.card:hover { background: color-mix(in oklab, var(--bg-deep), var(--ink) 6%); }
/* the Lucide glyph sits bare on the panel — the tinted red tile was the
   most template-looking part of the old card */
.svc-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.svc-ic {
  width: auto; height: auto;
  background: none; border: 0; border-radius: 0;
  display: block; color: var(--ink);
}
.svc-ic svg { display: block; width: 26px; height: 26px; stroke-width: 1.5; }
.svc h3 { font-size: 25px; letter-spacing: -0.015em; }
.svc p { font-size: 17.5px; color: var(--ink-soft); }
.svc .more {
  font-size: 16.5px; font-weight: 620; color: var(--ink); margin-top: 12px;
  display: inline-flex; align-items: center; gap: 8px;
}
.svc .more .arrow { transition: transform .18s ease; }
.svc:hover .more .arrow { transform: translateX(4px); }

/* ---------- approach (broker vs bank) ----------
   No photograph. The generated group portrait that held the left column is
   gone, and the section is a claim now, not a split: the headline runs the
   full measure with its second sentence in the hero's red italic, and the
   four points sit under it two by two in the services' filled-panel
   language — same surface, same radius, so the page's card grammar stays
   one thing. The old hairline-row treatment was right for a sidebar; at
   full width the rows read as a settings list. */
.approach { display: block; }
/* the payoff sentence carries the hero accent — italic serif, brand red,
   same voice as the h1's "found," */
.approach .h2-solo { max-width: 22ch; }
.approach .h2-solo em { font-weight: 580; color: var(--red); }
.approach-points { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; margin-top: 44px; }
.apoint {
  display: grid; grid-template-columns: 42px 1fr; gap: 18px; align-items: start;
  padding: 30px 32px 28px;
  background: var(--bg-deep); border-radius: 18px;
}
.apoint .glyph { width: 42px; height: 42px; border-radius: 50%; background: var(--accent-mid); color: var(--accent-deep); display: grid; place-items: center; font-family: var(--serif); font-size: 19px; font-weight: 600; }
.apoint h3 { font-size: 20px; font-family: var(--sans); font-weight: 640; letter-spacing: -0.01em; }
.apoint p { font-size: 17px; color: var(--ink-soft); margin-top: 5px; }

/* ---------- calculator embed — intro copy lives in the left column so the
   result card top-aligns with "Run your numbers" ---------- */
#calculator .h2-solo { margin-top: 12px; }
#calculator .calc-lede { margin-top: 14px; font-size: 19px; color: var(--ink-soft); max-width: 46ch; }
#calculator .svc-all { margin-top: 12px; }
#calculator .cfields { margin-top: 30px; }

/* ---------- journey — editorial split (headline left, serif prose right,
   the reference homepage's intro arrangement) ---------- */
.journey-grid { display: grid; grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr); gap: 28px 72px; align-items: start; }
.journey-grid h2 { font-size: 40px; line-height: 1.12; margin-top: 14px; }
.journey-prose { display: grid; gap: 18px; padding-top: 6px; }
.journey-prose p { font-size: 18.5px; line-height: 1.65; color: var(--ink-soft); }
@media (max-width: 900px) { .journey-grid { grid-template-columns: 1fr; gap: 20px; } }

/* ---------- testimonials ---------- */
/* Strips, not cards. Three padded boxes side by side is a lot of furniture
   for what is corroboration rather than argument, and stacked on a phone it
   was a long scroll. Each quote is now one hairline-separated row: the words
   on the left, who said them on the right. No panel, no block quote glyph —
   the mark leads the line instead of costing 40px above it. */
.quotes { display: grid; grid-template-columns: 1fr; gap: 0; }
.quote {
  /* <figure> carries a UA margin of 16.5px 40px — harmless behind a card
     background, but as a bare strip it inset every row and added ~33px of
     dead height to each one */
  margin: 0;
  display: grid; grid-template-columns: 1fr auto;
  align-items: baseline; gap: 4px 36px;
  padding: 10px 0;
  border-top: 1px solid var(--line-soft);
}
.quotes .quote:first-child { border-top: 0; }
.quote blockquote { margin: 0; font-family: var(--serif); font-size: 18px; line-height: 1.45; font-weight: 460; text-wrap: pretty; }
.quote blockquote::before { content: "“"; color: var(--accent); margin-right: 2px; }
/* attribution collapses onto one line and sits at the right edge */
.quote cite { font-style: normal; font-size: 13.5px; color: var(--ink-faint); white-space: nowrap; text-align: right; }
.quote cite strong { color: var(--ink); font-weight: 640; font-size: 13.5px; }
/* literal middot: a \00b7 escape eats the space that follows it as its delimiter */
.quote cite strong::after { content: " · "; color: var(--ink-faint); font-weight: 400; white-space: pre; }

@media (max-width: 760px) {
  /* the attribution drops under the quote rather than squeezing its measure */
  .quote { grid-template-columns: 1fr; gap: 6px; }
  .quote cite { text-align: left; white-space: normal; }
}

/* ---------- lenders (logo wall) ---------- */
.lenders { margin-top: 0; padding: 64px 0 60px; }
.lenders .eyebrow { color: var(--accent); }
.lenders-title { font-size: 30px; line-height: 1.14; max-width: 22ch; margin-top: 10px; }
/* support voice — sans 500, matching the hero statement and .section-head ledes */
.lenders-sub { margin-top: 12px; font-size: 21px; color: var(--ink-soft); font-family: var(--sans); font-weight: 500; }
/* "lenders" typewriter — JS (home.js) types the word when the section scrolls
   into view; without JS or with reduced motion the full word just shows */
.lenders-title .typed { color: var(--red); }
/* the JS reserves the finished word's width on .typed (inline-block +
   min-width), so the headline never re-wraps while letters type in; the
   caret is drawn INSIDE .typed via ::after so it rides the typed letters
   instead of blinking out at the reserved right edge */
.lenders-title .type-caret { display: none; }
.lenders-title.typing .typed::after {
  content: ""; display: inline-block; width: 3px; height: .82em;
  margin-left: 5px; border-radius: 2px; vertical-align: -0.06em;
  background: var(--red);
  animation: cm-caret 0.9s steps(1) infinite;
}
@keyframes cm-caret { 50% { opacity: 0; } }
/* the market the wall covers, said once and quietly — a taxonomy caption, not
   a second kicker, so it stays sentence case and drops the eyebrow's rule and
   uppercase; it groups DOWN onto the wall (big margin above, small below) */
.lender-cats {
  margin-top: 28px;
  font-family: var(--sans); font-size: 13px; font-weight: 540;
  letter-spacing: 0.06em; color: var(--ink-faint);
}
.lender-wall { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; margin-top: 30px; }
.lender-cats + .lender-wall { margin-top: 14px; }
.lender-logo {
  display: flex; align-items: center; justify-content: center;
  min-height: 78px; padding: 16px 14px;
  background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--radius);
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.lender-logo:hover { transform: translateY(-2px); box-shadow: var(--shadow-card); border-color: var(--line); }
.lender-logo img {
  max-height: 40px; max-width: 90%; width: auto; height: auto; object-fit: contain;
}
.lenders .foot { margin-top: 24px; font-size: 16px; color: var(--ink-soft); }

/* ---------- hero rate CTA glass card: see DARK ZONES below ---------- */
.hero-rate-cta .btn { flex-shrink: 0; }

/* ---------- explore-all-services link (in the services section head) ---------- */
.svc-all { margin-top: 2px; }
.svc-all a { font-size: 17px; font-weight: 640; color: var(--accent); }
.svc-all a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* ---------- renewal callout strip (slim) ---------- */
.renew-strip { display: grid; grid-template-columns: 1.4fr auto; gap: 24px 48px; align-items: center; padding: 38px 40px; }
.renew-strip h2 { font-size: 28px; line-height: 1.15; margin-top: 10px; }
.renew-strip p:not(.eyebrow) { color: var(--ink-soft); font-size: 15.5px; max-width: 58ch; margin-top: 10px; font-family: var(--sans); font-weight: 500; }
.renew-strip-ctas { display: grid; gap: 10px; }
.renew-strip-ctas .btn { justify-content: center; }

/* ---------- closing CTA ---------- */
.closing { text-align: center; display: grid; gap: 24px; justify-items: center; }
.closing h2 { font-size: 48px; max-width: 22ch; line-height: 1.1; }
.closing p { color: var(--ink-soft); font-size: 18px; max-width: 48ch; }
.closing p:not(.eyebrow) { font-family: var(--sans); font-weight: 500; }
.closing p.closing-links { font-size: 16.5px; margin-top: 2px; }
.closing-links a { text-decoration: underline; text-underline-offset: 3px; }

/* ---------- statement — oversized serif moment (the reference's big
   editorial statement band) ---------- */
.statement { text-align: center; }
.statement .eyebrow { justify-content: center; }
.statement-line {
  margin: 24px auto 0; max-width: 24ch;
  font-family: var(--serif); font-size: clamp(34px, 4.6vw, 56px);
  line-height: 1.22; letter-spacing: -0.015em; color: var(--ink); text-wrap: balance;
}
.statement-line em {
  font-style: normal;
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-decoration-thickness: .055em;
  text-underline-offset: .12em;
}

/* ============================================================
   DARK ZONES — hero, stats, rates, broker band, closing
   ============================================================ */

/* ---- hero (dark) ---- */
/* The bottom clamp is back: it is the band where the full-bleed photograph
   reads on its own below the type — road and sky, the calmest part of the
   frame. Without it the scrim has nowhere to release and the picture never
   actually shows. min-height keeps the ground present on wide, short-copy
   viewports. */
/* Taller, and with the bottom band trimmed. The reference composition is a
   near-full-screen frame: the picture runs from under the header to the fold
   and needs the height to be a PLACE rather than a strip. 58vh left it at
   ~520px on a laptop, which the copy alone nearly filled.

   The bottom padding came down at the same time (190 → ~80px). It used to be
   the band where the photograph read on its own below the type, back when
   the scrim washed the whole frame and this was the only place the picture
   got out from under it. The picture is clean across the right of the frame
   now, so all that band did on a desktop was leave ~130px of blank paper
   under the trust strip. */
/* The height and paddings are the tokens declared on .hero at the top of
   this file — see the note there. The short version: the stats band is the
   hero's own last child now, so the frame is a flex column — copy grid on
   top, band on the floor — and the fold lands on the band's bottom rule.
   The bottom padding moved off the section and onto the grid: the band has
   to reach the section's true bottom edge, and padding there would hold a
   strip of bare photograph under it. */
.hero.darkbg {
  padding: var(--hero-pad-top) 0 0;
  min-height: var(--hero-min);
  display: flex; flex-direction: column;
}
/* flex:1 is what floors the band: the grid takes every spare px of the
   frame, and the slack lands BELOW the intent row as clear photograph —
   the copy packs at the top now that nothing inside the column stretches
   (see .hero-intent). width:100% because .wrap's centering is margin-auto
   and a flex item otherwise shrinks to fit. */
.hero.darkbg > .hero-grid { flex: 1 0 auto; width: 100%; padding-bottom: var(--hero-pad-bottom); }
/* Above the photograph: .hero-media is a positioned layer, so the band's
   opaque ground needs its own stacking position to paint over it. */
.hero .stats { position: relative; z-index: 1; width: 100%; }
/* crisp, clean white banner (client feedback HP-2): the hero and the stats band
   drop the tinted base, colour glows and architectural grid — the photo and the
   type carry the section on plain white. */
.hero.darkbg, .stats.darkbg { --sec-base: var(--bg); }
.hero.darkbg::before, .hero.darkbg::after,
.stats.darkbg::before, .stats.darkbg::after { display: none; }
.stats.darkbg { background: var(--bg-deep); }
/* The kicker had a tag frame for the same reason the statement had a fill:
   at 14.5px in a deep brick red it was the most fragile run in the hero, and
   over the photograph it needed a backdrop of L≈0.41 to hold 4.5:1. On the
   flat page it measures about 10:1 unaided, so the frame is gone and it is
   simply a kicker again. */
.hero.darkbg .eyebrow {
  color: var(--brand-ink); letter-spacing: .16em; font-size: 14.5px;
}
@media (max-width: 560px) {
  .hero.darkbg .eyebrow { font-size: 11.5px; letter-spacing: .12em; gap: 8px; }
  .hero.darkbg .eyebrow::before { width: 16px; }
}
.hero.darkbg h1 { color: var(--on-dark); }
/* the accent keeps its brand red here — .darkbg used to force it back to the
   headline's ink because the emphasis was carried by the drawn rule, not by
   colour. It is the colour now, so this reasserts it over that inheritance. */
.hero.darkbg .hero h1 em, .hero.darkbg h1 em { color: var(--red); }
/* the statement reads at full ink, not the muted soft tone — it is the
   hero's second voice, not a caption under it */
.hero.darkbg .hero-sub { color: var(--on-dark); }
/* secondary button is now glass (see .btn-ghost in shared.css) */

/* The pools of light, the masked backdrop-filter focal plane, the flat
   open-air wash and the bottom release gradient all lived here — roughly
   150 lines whose entire job was making type legible over a photograph.
   The photograph is a panel now (see .hero-media at the top of this file)
   and nothing sits on it, so all of it is gone rather than retuned. */

/* ---- stats (dark, flows out of the hero) ---- */
.stats.darkbg { margin-top: 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.stats.darkbg .stat { border-left: 1px solid var(--dark-line); padding: 10px 28px; }
.stats.darkbg .stat:first-child { border-left: 0; padding-left: 0; }
.stats.darkbg .stat .n { color: var(--on-dark); }
.stats.darkbg .stat:nth-child(2) .n, .stats.darkbg .stat:nth-child(3) .n { color: var(--brand-ink); }
.stats.darkbg .stat .l { color: var(--on-dark-soft); }

/* ---- stats, tablet range (561–900): a tight 2x2 ----
   The 4-up rhythm loses its measure below ~900 and the phone list does not
   start until 560. In between, the bare `.stat` resets in the 900px block
   silently lost to .stats.darkbg .stat (0,3,0 — the same trap the phone
   block documents), so every cell kept its desktop border-left and 28px of
   left padding: the second row sat indented under the first and its divider
   hung over nothing. Specificity-matched rules, scoped to this range only
   so they cannot leak into the phone list below 561: odd cells flush left,
   one continuous centre divider down the even column, a single rule between
   the rows, and the chrome a step tighter again. */
@media (max-width: 900px) and (min-width: 561px) {
  .stats.darkbg .stat, .stats .stat { padding: 14px 24px; }
  .stats.darkbg .stat:nth-child(odd), .stats .stat:nth-child(odd) { border-left: 0; padding-left: 0; }
  .stats.darkbg .stat:nth-child(n+3), .stats .stat:nth-child(n+3) { border-top: 1px solid var(--dark-line); }
  .stats.darkbg .stat .n, .stats .stat .n { font-size: 26px; }
}

/* ---- closing CTA — anthropic-style dark feature block ----
   Deep charcoal, generous radius, inset from the page edges (the reference
   homepage's big dark panel). Dark-context tokens are re-lit locally since
   .darkbg maps them to ink for the light bands. */
.closing.darkbg {
  --sec-base: #171716;
  --darkbg-fade: #171716;
  --on-dark: #f5f4ef;
  --on-dark-soft: rgba(245, 244, 239, 0.8);
  --on-dark-faint: rgba(245, 244, 239, 0.62);
  --dark-line: rgba(245, 244, 239, 0.16);
  --cta-gutter: clamp(14px, 2.5vw, 32px);
  /* 0 = tucked in, 1 = fully open. site.js walks this as the panel scrolls up
     so the dark ground opens outward; the default is the finished state, so
     no-JS and reduced-motion readers land straight on it. */
  --cta-open: 1;
  background: transparent;
  border-radius: 0;
  /* full gap above; a shorter beat below so the panel sits near the footer
     now that it's the last block on the page */
  margin: var(--sect-gap) 0 48px;
  padding: 88px calc(var(--cta-gutter) + 24px);
}
/* Its buttons are white-space:nowrap by design, and "Start an application →"
   is wider than what 24px of inner padding leaves on a 320px phone — the
   panel was pushing 4px past the frame. The inner padding gives that back;
   the outer gutter, which is what insets the dark ground from the page edge,
   is untouched. */
@media (max-width: 374px) {
  .closing.darkbg { padding-left: calc(var(--cta-gutter) + 8px); padding-right: calc(var(--cta-gutter) + 8px); }
}
/* The dark ground lives on the pseudo-element rather than the section box, so
   it can grow on scroll via clip-path — no reflow of the copy sitting on it.
   `contain: paint` keeps the per-frame repaint inside the section instead of
   letting it invalidate a band of the page around it. */
.closing.darkbg { contain: paint; }
.closing.darkbg::before {
  background-color: var(--sec-base);
  clip-path: inset(
    calc((1 - var(--cta-open)) * 30px)
    calc(var(--cta-gutter) + (1 - var(--cta-open)) * 8vw)
    round calc(28px + (1 - var(--cta-open)) * 16px)
  );
}
/* Only hint the compositor while the panel is on screen and actually moving —
   home.js adds .cta-live on enter and drops it on exit, so an idle panel
   isn't holding a layer for the life of the page. */
.closing.darkbg.cta-live::before { will-change: clip-path; }
.closing.darkbg::after { display: none; } /* the faint grid is tuned for light bands */
.closing.darkbg .eyebrow { color: var(--red-bright); }
/* glass button re-lit for the dark panel */
.closing.darkbg .btn-ghost {
  --gh-text: #f5f4ef;
  --gh-bg: rgba(245, 244, 239, 0.08);
  --gh-bg-hover: rgba(245, 244, 239, 0.16);
  --gh-border: rgba(245, 244, 239, 0.3);
  --gh-hi: rgba(255, 255, 255, 0.14);
}
.closing.darkbg h2 { color: var(--on-dark); }
.closing.darkbg p { color: var(--on-dark-soft); }
.closing.darkbg p.closing-links { color: var(--on-dark-faint); }
.closing.darkbg .closing-links a:hover { color: var(--on-dark); }
/* ---- closing CTA — paper panel variant (homepage) ----
   The homepage closing sits mid-page on warm paper now (no second dark
   ground), which silently retired the open-outward reveal: the dark panel's
   ground WAS the animation, and a transparent ground has nothing to open.
   The reveal comes back on the page's own deep-paper tone — the same surface
   the service cards and the stats band stand on — so the panel still opens
   into the page as it scrolls up without re-darkening it. Same clip-path
   machinery as .closing.darkbg above; home.js drives either class. */
.closing.paperbg {
  position: relative;
  /* the ::before sits at z-index:-1, and isolation keeps that behind the
     copy but still above the page ground */
  isolation: isolate;
  contain: paint;
  /* .section's padding is top-only (the next section's top padding makes the
     page rhythm) — fine on plain paper, but this section paints its own
     ground, so without a bottom half the last line stood flush on the
     panel's bottom edge with a full --sect-gap of air above the eyebrow.
     Symmetric padding centers the copy in the panel. */
  padding-bottom: var(--sect-gap);
  --cta-gutter: clamp(14px, 2.5vw, 32px);
  /* 0 = tucked in, 1 = fully open; default is the finished state so no-JS
     and reduced-motion readers land straight on it */
  --cta-open: 1;
}
.closing.paperbg::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-color: var(--bg-deep);
  clip-path: inset(
    calc((1 - var(--cta-open)) * 30px)
    calc(var(--cta-gutter) + (1 - var(--cta-open)) * 8vw)
    round calc(28px + (1 - var(--cta-open)) * 16px)
  );
}
.closing.paperbg.cta-live::before { will-change: clip-path; }

/* When the closing panel IS the last block it meets the dark footer directly
   and the footer keeps its own margin off. On the homepage it now sits
   mid-page (right after "Why choose us"), so both of those have to be
   conditional on it actually being last — otherwise the footer loses its gap
   on a page whose final section is something else entirely. */
body:has(main > .closing.darkbg:last-child) .site-footer { margin-top: 0; }
/* mid-page: the section that follows brings its own var(--sect-gap) of top
   padding, so the panel's 48px would just add to it */
main > .closing.darkbg:not(:last-child) { margin-bottom: 0; }

/* Below ~1000px the seven/five split leaves neither column enough measure,
   so the hero stacks: the copy in document order, then the photograph
   beneath it as a wide panel rather than a standing one. */
@media (max-width: 1000px) {
  .hero-sub { font-size: 20px; }
}

@media (max-width: 900px) {
  /* The photo stays on phones: the hero is the most distinctive thing on the
     page and dropping it made the phone read as a different, plainer site.
     In its own panel it costs the copy nothing. */
  .hero h1, body[data-hero="editorial"] .hero h1 { max-width: none; }
  .approach-points { grid-template-columns: 1fr; }
  body[data-hero="editorial"] .hero-copy { grid-column: 1; grid-row: auto; }
  .svc-grid, .quotes { grid-template-columns: 1fr; }
  .stats .wrap { grid-template-columns: 1fr 1fr; }
  /* the per-cell border/padding fixes live in the 561–900 range block by
     the darkbg stats rules — a bare .stat here loses to them on specificity */
  body[data-hero="editorial"] .rates-rows { grid-template-columns: 1fr; }
  body[data-hero="editorial"] .rate { border-left: 0; padding-left: 0; }
  .lender-wall { grid-template-columns: repeat(3, 1fr); }
  .renew-strip { grid-template-columns: 1fr; padding: 28px 24px; }
  .renew-strip-ctas { justify-items: start; }
}

/* ---------- phones: same ground, different scrim geometry ----------
   The backdrop itself is global now; this block only retunes it for one
   narrow column. With no left/right geometry to track, the horizontal ramp
   is replaced by a straight vertical one, and the clear band moves to the
   BOTTOM of the section instead of the right edge. */
@media (max-width: 767px) {
  /* The clear band, sized by the PRINT rather than by a round number. The
     master is 0.5586 tall for its width at every rung of the ladder, and
     contain fits it to the frame's width, so its rendered height is always
     55.86vw. The old clamp(160px, 42vw, 240px) was short of that at every
     phone width — at 375px it reserved 160px for a 210px print, so the top
     ~50px of the picture slid up behind the intent links, where the
     near-solid scrim buried it and the links stood on a hazy slice of
     rooftops. The band now holds the WHOLE print plus a small breathing
     joint between the copy's last line and the top edge of the picture. */
  .hero.darkbg { --img-h: 55.86vw; min-height: 0; }
  /* The band is reserved on the GRID's padding, not the section's: the
     figure's box is the grid now, and the stats band is the section's
     last child, so this is what puts the print BETWEEN the copy and the
     figures — copy, then the street running down to the band's top rule,
     then the numbers. Section padding-bottom here would instead open a
     strip of bare paper under the stats band. */
  .hero.darkbg > .hero-grid { padding-bottom: calc(var(--img-h) + clamp(20px, 6vw, 36px)); }
  /* cover is wrong on a phone. The ground is a wide band — the impression
     runs the full width of its frame — and cover-fitting that into a portrait
     frame keeps the height and throws away most of the width, leaving a
     vertical slice of one shopfront.

     contain fits the whole band to the phone's width instead, and 100% sits
     it on the section's bottom edge, so the street runs off the foot of the
     hero rather than floating above a strip of leftover paper. It lands in
     the one place it can be read: the clear band at the foot of the scrim
     below, which goes near-solid 175px up. No transform and no filter — the
     master is white and composed to be used whole. */
  .hero-media img {
    object-fit: contain;
    object-position: 50% 100%;
  }
  /* Anchored to the PRINT's top edge, not to fixed px from the bottom. The
     old 55/115/175px stops were tuned for a 160px band; with the band now
     tracking the print's real height (see --img-h above) fixed stops would
     leave most of the picture under the wash on a wide phone and none of it
     covered on a narrow one. Measured from the bottom, the print's top edge
     IS var(--img-h): the picture runs fully clear through its lower bulk,
     the fade spans only its top ~50px — sky and rooflines, the part with the
     least to say — and the paper is back to near-solid a few px above the
     print, inside the breathing joint, so every line of copy stands on
     near-solid ground. */
  .hero-media::after {
    background: linear-gradient(to top,
      transparent 0,
      transparent calc(var(--img-h) - 52px),
      color-mix(in oklab, var(--bg), transparent 82%) calc(var(--img-h) - 26px),
      color-mix(in oklab, var(--bg), transparent 38%) calc(var(--img-h) - 6px),
      color-mix(in oklab, var(--bg), transparent 4%) calc(var(--img-h) + 14px),
      color-mix(in oklab, var(--bg), transparent 4%) 100%);
  }
}

@media (max-width: 560px) {
  .lender-wall { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- calculator — split entrance ----------
   The panel opens along its centre line: the inputs column drops in from
   above while the result card rises from below, meeting in place. The
   from-state is only ever applied by JS (.calc-split), so no-JS readers and
   crawlers get a panel that is simply there. */
@media (prefers-reduced-motion: no-preference) {
  .cpanel-grid.calc-split > * {
    opacity: 0;
    transition: opacity .78s cubic-bezier(.16, 1, .3, 1),
                transform .78s cubic-bezier(.16, 1, .3, 1);
  }
  .cpanel-grid.calc-split > :first-child { transform: translateY(-38px); }
  /* the result card trails by a beat, so the two halves read as one movement
     closing rather than a symmetrical snap */
  .cpanel-grid.calc-split > .cresult { transform: translateY(38px); transition-delay: .1s; }
  .cpanel-grid.calc-split.calc-in > * { opacity: 1; transform: none; }

  /* stacked on phones — counter-motion only reads as a split when the two
     halves are actually side by side, so there both simply rise */
  @media (max-width: 900px) {
    .cpanel-grid.calc-split > :first-child,
    .cpanel-grid.calc-split > .cresult { transform: translateY(26px); }
  }
}

/* ---------- calculator disclosure ----------
   The panel starts folded behind a single button. Anthropic's disclosures
   are quiet: a plain pill, a mark that rotates rather than swaps, and a
   height that eases rather than snaps — the motion tells you the panel is
   part of the page, not an overlay arriving on top of it.

   Height animates via grid-template-rows 0fr → 1fr, which interpolates to
   the content's real height without hard-coding a max-height that would
   clip the panel the moment the copy or a breakpoint changes it. */
.calc-head { display: grid; justify-items: start; }
.calc-toggle {
  margin-top: 26px;
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--sans); font-size: 15px; font-weight: 620;
  color: var(--ink); background: var(--bg-deep);
  border: 0; border-radius: 999px;
  padding: 13px 16px 13px 24px;
  cursor: pointer;
  transition: background .2s ease;
}
.calc-toggle:hover { background: color-mix(in oklab, var(--bg-deep), var(--ink) 7%); }
.calc-toggle:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
/* the mark is a plus that becomes a minus — one stroke rotates away, so the
   state change reads as a single continuous move rather than an icon swap */
.calc-toggle-mark {
  position: relative; width: 22px; height: 22px; flex-shrink: 0;
  border-radius: 50%; background: var(--ink);
}
.calc-toggle-mark::before,
.calc-toggle-mark::after {
  content: ""; position: absolute; inset: 50% 6px auto 6px;
  height: 1.5px; margin-top: -.75px;
  background: var(--bg); border-radius: 1px;
}
.calc-toggle-mark::after { transition: transform .34s cubic-bezier(.16, 1, .3, 1); }
.calc-toggle[aria-expanded="false"] .calc-toggle-mark::after { transform: rotate(90deg); }

.calc-reveal {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .52s cubic-bezier(.16, 1, .3, 1);
}
.calc-reveal[data-open] { grid-template-rows: 1fr; }
.calc-reveal-inner { overflow: hidden; min-height: 0; }
/* the panel needs air once it is open, but that padding must not hold the
   folded state open, so it lives on an inner box that collapses with it */
.calc-reveal .cpanel-grid { padding-top: 40px; }

@media (prefers-reduced-motion: reduce) {
  .calc-reveal { transition: none; }
  .calc-toggle-mark::after { transition: none; }
}

/* ---------- phone polish ---------- */
@media (max-width: 560px) {
  /* the two hero buttons wrap to different widths and read as ragged; as a
     full-width pair they stack cleanly and give a bigger tap target */
  .hero-ctas { display: grid; grid-template-columns: 1fr; gap: 10px; width: 100%; }
  .hero .hero-ctas { gap: 10px; }
  .hero-copy > .hero-ctas { justify-self: stretch; }
  .hero-ctas .btn { justify-content: center; width: 100%; }
  /* The link centres under the button rather than sitting at the column's
     left edge. Stacked, the button is a full-width block with centred text,
     and a left-aligned link beneath it reads as a stray line of copy that
     lost its row — the two are one control group and share a centre line.
     justify-self centres the LINK BOX in the grid track; justify-content
     centres the text and arrow inside it. */
  .hero-ctas .hero-link { justify-self: center; justify-content: center; padding: 4px 8px; }

  /* The stats band is a 4-up rhythm that does not survive being folded into
     two 173px columns: ~117px of measure broke "Years of combined leadership
     experience" over four lines and left the boxes ragged and unequal. On a
     phone it reads far better as a list — figure left, label right, hairline
     between — which also puts all four on one scale instead of two.

     Specificity-matched to .stats.darkbg .stat (0,3,0) on purpose: a bare
     .stat here silently loses to it, which is exactly how the earlier
     attempt at this rule failed to apply at all. */
  .stats .wrap { grid-template-columns: 1fr; }
  .stats.darkbg .stat,
  .stats .stat {
    display: grid;
    grid-template-columns: 4.6rem 1fr;
    align-items: baseline;
    gap: 2px 18px;
    padding: 14px 0;
    border-left: 0;
    border-top: 1px solid var(--line-soft);
  }
  .stats.darkbg .stat:first-child,
  .stats .stat:first-child { border-top: 0; }
  /* figures right-aligned so their edges line up and every label starts at
     the same x — ragged-left numbers were what made the list look untidy */
  .stats.darkbg .stat .n, .stats .stat .n { font-size: 27px; text-align: right; }
  .stats.darkbg .stat .l, .stats .stat .l { font-size: 16px; line-height: 1.45; }

  /* Lighter, cooler ground, and one tone for every figure. The desktop band
     alternates ink and brand across four columns, which reads as rhythm in a
     row and as randomness in a stacked list. Selectors are matched to the
     nth-child rules that set the brand colour, which are 0,4,0. */
  .stats.darkbg { background: #f6f6f7; }
  .stats.darkbg .stat .n,
  .stats.darkbg .stat:nth-child(2) .n,
  .stats.darkbg .stat:nth-child(3) .n { color: var(--ink); }

  /* card padding tuned for a wide grid cell is too generous on one column */
  .svc { padding: 26px 22px 24px; }
  .svc h3 { font-size: 22px; }
}
