/* ═══════════════════════════════════════════════════════════════════════════
   HOMEPAGE HERO CARD + TOOL BENTO GRID
   ---------------------------------------------------------------------------
   Replaces the animated looped-video hero (`.hero.visual-hero` +
   `.qf-hero-composite` laptop/phone clips) with the measured reference
   structure: a TINTED ROUNDED CARD holding the headline, the company-name
   conversion field and a 3-up action grid, followed by a second section that
   lists our free tools as a 2-up bento grid.

   WHY A NEW FILE, AND WHY NEW CLASS NAMES.  The homepage stacks ~20 sheets and
   seven of them re-declare `.hero`, `.hero-copy`, `.hero-grid`, `h1` and
   `.lead` with `!important`; four of those are injected by landing-motion.js
   AFTER first paint, so they out-cascade anything linked in <head>. Rather
   than fight that, the carrier hero drops `.hero`/`.visual-hero`/`.hero-copy`
   entirely and is rebuilt under `.qf-hhero`, which nothing else in the bundle
   knows about. A brand-new file also means the two CI guards' `file:line`
   baselines do not renumber (docs/design-refactor/baseline-protocol.md, r1).

   DESIGN LAW.  Tokens only; zero gradients, zero backdrop-filter, zero
   keyframes; three shadows; radii 6/8/12; motion `all .3s ease` on controls
   only. Spacing sits on the house ramp {0,4,8,12,16,24,32,48,60,80,120}, so
   the reference's 20px grid gap is rendered as 24px, its 76px hero padding as
   80px, and its 40px mobile hero padding as 32px.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · The hero card ───────────────────────────────────────────────────── */

.qf-hhero {
  display: block;
  margin: 0;
  padding: 0;
}

/* The card itself: a soft blue WASH, 12px radius, a ~14px page rail down both
   sides and 8px of page ground above it.

   TWO decorative rasters, both generated (style.css EOF explains how and why
   they are rasters and not CSS gradients — the design law forbids gradients).
   Both sit OVER the flat `--surface-hero` colour, so a failed or unsupported
   image leaves the card on the tint it had before, never white.

   Layer order is top-to-bottom in the shorthand:

   1-2. `--surface-hero-grain` + `--surface-hero-grain-ink` — a matched PAIR of
      256×256 film-grain tiles, `repeat` at their NATURAL size. One is white at
      a varying alpha, the other black, and exactly one of them is opaque at
      any given pixel, so together they are a single bidirectional grain field
      that works whatever it sits on: the white half carries the texture on the
      saturated top, the black half carries it on the near-white bottom. (Two
      files rather than one RGBA field is an ENCODER decision — WebP stores
      alpha losslessly and colour lossily, so a random black-and-white colour
      plane is the most expensive thing you can hand it: 67 KB as one tile
      against 36 KB as two. See scripts/make-hero-wash.mjs.)

      They must not be part of the wash. The wash is RESAMPLED to the card's
      width — 1440 up to 2509 at a 2560 viewport, 1440 down to 368 at 375 —
      and resampling averages per-pixel texture straight out of existence.
      Tiled at 1:1 they are identical at every card width, and they carry on
      over the flat base colour so the texture never stops where the raster
      does. One pair serves both themes.
   3. `--surface-hero-image` — the wash: a VERTICAL FADE from saturated AZURE
      at the top to the near-white `--surface-hero` at the bottom, painted
      `100% 1240px` at `0 0`. The azure is the owner's hero reference sampled
      (#0179AD, hue 198) and is NOT `--accent` (#3356EE, hue 229); see the
      Wave 5 block in style.css.

      NOT `cover`, and that is the whole reason this card used to wear a
      centre-lit vignette instead of a fade. `cover` crops whichever axis is
      surplus: at 2560 the card is 2509x839 and a 2560x1100 field anchored at
      `50% 100%` showed only its BOTTOM 65%, so a saturated top would simply
      not have been on screen. `100% 1240px` at `0 0` pins raster row N to card
      row N at EVERY width instead, which is what lets the stops be chosen
      against where the copy actually sits (the band climbs 0→420, the ramp
      runs 420→760 — see the generator). 1240 is taller than the tallest card
      measured (1200px at 375), so the raster covers the card outright at every
      width tested.

   THE HAIRLINE IS STILL LOAD-BEARING, but only at ONE end now, and the brief
   asked the question explicitly. At the TOP the card announces itself: the
   #026A98 band against the #F8FAFC page ground is a 6.1:1 step, and the 12px
   corners read on their own with the rim removed — it is genuinely redundant
   there. At the BOTTOM the fade has landed on `--surface-hero`, which is now
   TRUE WHITE and 1.06:1 against the page. That is not an edge by any measure,
   so the lower corners and the rails beside the action cards exist ONLY
   because of the 1px `--border-strong` rim — which is also what lets the tail
   be pure white without the bento cards (`--surface`, also white) dissolving
   into it: they carry their own rim and shadow.

   So it stays, on all four sides. A rim that fades out half way down the card
   is a worse artefact than a redundant one: `border-image` is the only way to
   express it, it would have to track the fade stops in a second place, and the
   top 475px is exactly where nobody is looking for an edge anyway. */

/* THE 8px OF PAGE GROUND ABOVE THE CARD IS DELIBERATE, and it is the second
   half of the "the menu bar has a border" fix. The card used to start at
   exactly `header.bottom`, so its own `--border-strong` top hairline landed on
   the header's bottom edge and ran 98% of the viewport width — a full-bleed
   1px line immediately under the bar, which is a header border in every way
   that matters to the eye. Eight pixels of `--bg` between them is all it takes:
   the hairline now reads as the top edge of a card that is floating in the
   page, the corners' 12px radius is visible on both sides of it, and the bar's
   own bottom edge is bare ground at every x. It matches the 8px inset the
   page frame already uses for its rails. */
.qf-hhero__card {
  background-color: var(--surface-hero);
  background-image: var(--surface-hero-grain), var(--surface-hero-grain-ink), var(--surface-hero-image);
  background-repeat: repeat, repeat, no-repeat;
  background-position: 0 0, 0 0, 0 0;
  background-size: auto, auto, 100% 1240px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  max-width: 98%;
  margin: var(--space-1) auto 0;
  padding: 80px 0;
}

/* Content column inside the card — the 1328/24/1280 container geometry. */
.qf-hhero__inner {
  width: 100%;
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-gutter);
}

/* Audience segmented control sits at the top of the card. `.hero-grid` on the
   row is kept (navInformationArchitecture pins that markup) but its two-column
   hero layout is neutralised here — inside the card the row is just a flex line.
   Specificity (0,3,2) + !important beats the injected sheets.

   IT IS THE EYEBROW, SO IT IS TOP-LEFT. This control is what the two audience
   panels dropped their `.eyebrow` label FOR (see the note in §2), which makes it
   the first thing in the header block and puts it under the same rule: eyebrows
   top-left. It was the last centred item in the hero after the h1, the lead and
   the CTA row moved left, and one centred control floating above a left-aligned
   stack reads as a mistake rather than as a choice. */
html body.qf-wft .qf-hhero .qf-aud-togglerow {
  display: flex !important;
  grid-template-columns: none !important;
  justify-content: flex-start !important;
  align-items: center !important;
  gap: 0 !important;
  width: 100% !important;
  max-width: none !important;
  margin: 0 0 32px 0 !important;
  padding: 0 !important;
}
html body.qf-wft .qf-hhero .qf-aud-togglerow > .qf-aud-toggle {
  justify-self: start !important;
  margin: 0 !important;
}

/* ── 2 · Headline block ──────────────────────────────────────────────────── */

/* LEFT-ALIGNED AT EVERY WIDTH. The house rule is that page and card headers are
   never centred — eyebrows top-left, titles left — and every other page already
   overrides the shared `.hero`'s default centring to get there (directory,
   glossary, compliance, profile). This hero did not, and the result was also
   internally inconsistent: ≥701 computed `center` while the ≤640 branch below
   computed `left`, so the same heading obeyed the rule on a phone and broke it
   on a laptop. The ≤640 branch is now the behaviour at all widths and its
   duplicate declarations have been removed rather than left to shadow these.

   `margin: 0`, NOT `0 auto`, IS HALF THE FIX. Text-align alone would leave a
   left-ragged 780px block still centred inside the 1280px content column — the
   h1 would start 250px in from the card's left edge (measured) rather than on
   the same vertical as everything below it. Zeroing the auto margin is what
   puts the title on the content column's left edge. Same for `.qf-hhero__action`
   below, which carried the matching `margin: 12px auto 0`. */
.qf-hhero__head {
  max-width: 780px;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  text-align: left;
}

/* NO EYEBROW RULES HERE ANY MORE. Both audience panels dropped their eyebrow
   (the segmented toggle above states the audience already), so the block that
   stripped the shared `.eyebrow` pill chrome back to a plain uppercase label
   went with them rather than sitting here matching nothing. */

html body.qf-wft .qf-hhero__head h1 {
  margin: 0;
  padding: 0;
  max-width: 780px;
  font-family: var(--font-sans);
  font-size: 54px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -2.16px;
  color: var(--hero-wash-ink);
  text-align: left;
  text-wrap: balance;
}

html body.qf-wft .qf-hhero__lead {
  margin: 0;
  padding: 0;
  max-width: 726px;
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: -0.18px;
  color: var(--hero-wash-ink);
  text-align: left;
}

/* ── 3 · The conversion hook: company-name finder ────────────────────────── */
/* This is the site's primary CTA — type your company, we pull your FMCSA
   record and open the demo calculator branded as YOU. Same markup, same ids,
   same handler (landing-carrier-finder.js); only the shell is restyled from a
   glass card to a plain field on the tint. Specificity (0,3,2)+!important is
   required to beat landing-glass.css, which is injected after this sheet. */
.qf-hhero__action {
  width: 100%;
  max-width: 520px;
  margin: 12px 0 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}
/* The legacy hero stacked these three in a flex column and hand-ordered them
   (`.qf-finder{order:4}`, `.hero-cta{display:contents}`, trust line at
   `margin-top:18px`). Inside the new card the DOM order IS the visual order, so
   every inherited order/offset is zeroed here in one place. */
html body.qf-wft .qf-hhero__action > * {
  order: 0 !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  width: 100%;
}

html body.qf-wft .qf-hhero .qf-finder {
  width: 100%;
  max-width: 520px;
  margin: 0 !important;
  padding: 0 !important;
  background: transparent !important;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  text-align: left;
}
html body.qf-wft .qf-hhero .qf-finder:hover {
  box-shadow: none !important;
  transform: none !important;
}
html body.qf-wft .qf-hhero .qf-finder__input {
  width: 100%;
  min-height: 56px;
  padding: 8px 16px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink);
  transition: var(--motion-state);
}
html body.qf-wft .qf-hhero .qf-finder__input::placeholder { color: var(--muted); }
html body.qf-wft .qf-hhero .qf-finder__input:focus {
  outline: none;
  border-color: var(--border-focus);
  box-shadow: var(--focus-ring);
}
/* The two helper lines under the field ("No sign-up. We pull your details from
   public FMCSA records." and "Set up in ~5 minutes · sourced from FMCSA public
   data.") are gone from the markup, so their rules are gone from here. */

/* `flex-start`, not `center` — the CTA row is part of the header block and
   follows it left, at every width. It used to be centred above 640 and
   flex-start below it, which is the same split the h1 and lead had. */
html body.qf-wft .qf-hhero .hero-cta {
  display: flex !important;
  justify-content: flex-start !important;
  margin: 0 !important;
  padding: 0;
}

/* THE SECONDARY ROUTE TO SIGNUP is a text line under the lead, NOT a second
   button. The hero is allowed exactly one accent-filled CTA (`--hero-cta-fill`,
   applied by `.hero-cta .btn` in §below) and "Show me my calculator" is it; a
   `.btn` here would have picked that same fill up and given the hero two
   competing primaries.

   `--hero-wash-ink`, the band's own ink, at FULL strength and !important. Both
   halves of that are load-bearing:

   • !important because four sheets in this bundle are injected after this one
     and set `.qf-hhero p`-ish colors; without it this line resolved to
     `--muted` (rgb(71,84,103)) in light, measured 2.66:1 on the band.
   • full strength because a dimmed variant is the one thing on this card that
     would drop under AA, and there is no muted-on-wash token to borrow.

   Placement is the other half of the contrast answer and is written up in the
   markup: the rows BELOW the CTA sit in the wash's fade, where neither white
   nor dark ink clears 4.5:1 across a two-line block. This line sits above the
   field, on the flat part of the band.

   Focus ring is `--hero-wash-focus` for the same reason the field's is:
   `--focus-ring` is an 8%-opacity blue and is nothing at all on a blue ground. */
html body.qf-wft .qf-hhero .qf-aud-panel .qf-hhero__alt {
  max-width: 620px;
  margin: 12px 0 0 !important;
  font-family: var(--font-sans) !important;
  font-size: 14px !important;
  font-weight: 400 !important;
  line-height: 1.5 !important;
  color: var(--hero-wash-ink) !important;
  text-align: left !important;
}
/* THE LINK NEEDS (0,7,3), and that number is not padding. quotefleet-color-system.css
   loads EIGHT sheets after this one and carries
   `html[data-theme="light"] body.qf-wft a:not(.btn):not(.nav-link):not(.signin):not(.demo-link)`
   → `color: var(--accent) !important`, which is (0,6,3). At (0,4,3) this rule's
   own !important lost the tie-break and the link rendered indigo on the azure
   band at 1.23:1 — measured, not assumed. `.qf-hhero__head` + `[href]` +
   `:not(.btn)` take it to (0,7,3), one step clear, without an id and without
   touching the global rule. */
html body.qf-wft .qf-hhero .qf-aud-panel .qf-hhero__head .qf-hhero__alt a[href]:not(.btn) {
  color: var(--hero-wash-ink) !important;
  text-decoration: underline !important;
  text-underline-offset: 2px;
  font-weight: 600 !important;
  /* Keep the link whole. At 375 the line wraps, and without this it broke as
     "Or" / "start your 14-day trial →" — an orphaned word carrying the
     underline. The link is ~200px, so it always fits a 320px column on its own
     row. */
  white-space: nowrap;
}
html body.qf-wft .qf-hhero .qf-aud-panel .qf-hhero__head .qf-hhero__alt a[href]:not(.btn):hover {
  text-decoration-thickness: 2px !important;
}
html body.qf-wft .qf-hhero .qf-aud-panel .qf-hhero__alt a:focus-visible {
  outline: none;
  border-radius: var(--radius-sm);
  box-shadow: var(--hero-wash-focus);
}

/* ── 4 · The 3-up action grid, INSIDE the hero card ──────────────────────── */

.qf-hhero__cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  margin: 48px 0 0 0;
  padding: 0;
  list-style: none;
}

/* `min-width: 0` IS LOad-BEARING, not tidiness. A grid track sized `1fr` still
   floors at its item's min-content width, and once the action bar stopped
   wrapping, a card's min-content became the SUM of its two nowrap buttons. At
   320px that is wider than the column, so the track grew past the viewport and
   the whole document scrolled sideways — 19px of it, caught by
   heavy-haul-quote.spec's ZERO HEADER OVERFLOW sweep. With the floor released
   the track obeys the container, and the ≤374px rule further down makes sure
   there is nothing left to clip at the widths where it would have mattered. */
.qf-hcard {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto 1fr auto;
  align-items: start;
  justify-items: start;
  row-gap: 16px;
  column-gap: 16px;
  min-width: 0;
  min-height: 192px;
  padding: 24px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  overflow: clip;
  text-align: left;
}

/* DARK THEME — `--surface-hero` and `--surface` resolve to the same #131A28 in
   dark, so the hero's action cards sat invisibly on the hero card's own ground.
   Push the cards one step up the surface ramp; light is untouched. */
html[data-theme="dark"] .qf-hcard,
:root:not([data-theme="light"]) .qf-hcard {
  background: var(--surface-2);
}

/* ── 5 · The tool bento section ──────────────────────────────────────────── */

/* `.section` carries a horizontal gutter of its own in three sheets that load
   after this one; the inner container owns the gutter here, so it is zeroed at
   the section so the tool grid lines up with the hero card's content column. */
html body.qf-wft.landing-v2 .qf-toolgrid-section {
  padding: 80px 0 60px !important;
}

.qf-toolgrid-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  margin: 0 0 32px 0;
}
html body.qf-wft .qf-toolgrid-head h2 {
  margin: 0;
  padding: 0;
  font-family: var(--font-sans);
  font-size: 32px;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -1.28px;
  color: var(--ink);
  text-align: left;
  /* Carried for the same no-orphan reason as its two siblings on this band,
     though this heading is the one that does NOT currently strand a word at any
     width — it breaks 2+3 at 375 and 4+5 at 320. It is here so the four
     headings on the page balance identically and a copy edit cannot quietly
     reintroduce the orphan the other three were fixed for. */
  text-wrap: balance;
}
/* The category noun is de-emphasised so the sentence, not the label, leads.

   `--muted`, NOT `--muted-soft` — the soft step was simply the wrong token for
   a heading and it failed AA at every width this half ships at. Measured off
   the rendered page: `--muted-soft` is #90A1B9 on `--bg` #F8FAFC = 2.51:1 in
   light, under even the 3:1 large-text floor at 32/28px; in dark it is #64748B
   on #0C111D = 3.96:1, which clears 3:1 but not the 4.5:1 this half needs once
   the ≤640 breakpoint takes it to 22px and it stops being "large text".
   `--muted` measures 7.35:1 light / 7.17:1 dark on the same ground.

   That this is a token slip and not a deliberate choice is settled by the
   sibling: `.qf-partner-title__soft` runs the identical two-tone idiom one
   section down and has always used `--muted` (6.97:1 on the partner card's
   own ground). The de-emphasis is unharmed — `--ink` is 18.02:1 here, so
   `--muted` is still an unmistakable step down from the lead phrase. */
html body.qf-wft .qf-toolgrid-head h2 .qf-toolgrid-head__soft { color: var(--muted); }

.qf-toolgrid-head__all {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
  /* `.2s`, and only the property that actually moves — see `.qf-toolbtn` §6. */
  transition: var(--motion-state);
  transition-property: color;
}
html body.qf-wft .qf-toolgrid-head__all { color: var(--ink) !important; }
html body.qf-wft .qf-toolgrid-head__all:hover { color: var(--accent) !important; }
.qf-toolgrid-head__all:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.qf-toolgrid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 24px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Recessed variant — border matches the ground so the 1px is reserved and
   nothing shifts, and NO shadow (the raised hero card carries the elevation). */
.qf-toolcard {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto 1fr auto;
  align-items: start;
  justify-items: start;
  row-gap: 16px;
  column-gap: 16px;
  min-width: 0;
  min-height: 192px;
  padding: 24px;
  background: var(--surface-2);
  border: 1px solid var(--surface-2);
  border-radius: var(--radius);
  box-shadow: none;
  overflow: clip;
  text-align: left;
}

/* ── 5b · The free-ladder band ───────────────────────────────────────────
   Same recessed-card metric as `.qf-toolcard` (24px padding, --surface-2 on a
   border of its own colour so the 1px is reserved, no shadow — the raised hero
   card owns the page's elevation). Three items, so the 3-up never strands a
   single orphan, and it collapses straight to one column at 700 with the rest
   of the page's grids. No `.btn` anywhere on this band: the hero holds the
   page's one accent-filled control. */
.qf-free-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  margin: 0 0 32px 0;
}
html body.qf-wft .qf-free-head h2 {
  margin: 0;
  padding: 0;
  font-family: var(--font-sans);
  font-size: 32px;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -1.28px;
  color: var(--ink);
  text-align: left;
  /* Same no-orphan balance as the three sibling headings on this page. */
  text-wrap: balance;
}
html body.qf-wft .qf-free-head h2 .qf-free-head__soft { color: var(--muted); }

.qf-free-list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.qf-free-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
  padding: 24px;
  background: var(--surface-2);
  border: 1px solid var(--surface-2);
  border-radius: var(--radius);
  box-shadow: none;
  text-align: left;
}
html body.qf-wft .qf-free-item h3 {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: -0.2px;
  color: var(--ink);
  text-align: left;
}
html body.qf-wft .qf-free-item p {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 400;
  line-height: 1.6;
  color: var(--ink-soft);
  text-align: left;
}

/* Pushed to the card's floor so the three links sit on one line whatever the
   descriptions above them do. `margin-top: auto` is the gap doing the work; no
   divider rule is drawn. */
.qf-free-link {
  margin-top: auto;
  padding-top: 8px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.5;
  text-decoration: none;
  white-space: nowrap;
  transition: var(--motion-state);
  transition-property: color;
}
/* INK AT REST, ACCENT ON HOVER — the same way round as `.qf-toolgrid-head__all`
   on the band below, and for a measured reason: `--accent` on the recessed
   `--surface-2` these cards sit on samples 5.14:1 in light but 2.78:1 in DARK,
   which fails AA on 15px text. `--ink` clears it in both themes, the arrow
   plus the underline-on-hover carry the affordance, and hover/focus are not
   load-bearing for contrast. */
html body.qf-wft .qf-free-link { color: var(--ink) !important; }
html body.qf-wft .qf-free-link:hover { color: var(--accent) !important; text-decoration: underline; text-underline-offset: 2px; }
.qf-free-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

html body.qf-wft .qf-free-then {
  max-width: 720px;
  margin: 24px 0 0 0;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 400;
  line-height: 1.6;
  color: var(--ink-soft);
  text-align: left;
}
html body.qf-wft .qf-free-then a { color: var(--accent) !important; font-weight: 500; }

/* ── 6 · Card internals — shared by both grids ───────────────────────────── */

/* THE CARD IS A GRID, AND THE ICON SITS ON THE TITLE'S LINE.

   The owner's ask, from a 375px screenshot: the tile and the title read as one
   header, not as two stacked rows with the tile floating alone above a short
   title. So the card's three flex rows become a two-column grid:

     col 1 (auto)          col 2 (minmax(0, 1fr))
     ┌──────────────┬────────────────────────────┐
     │  icon tile   │  h3  (both centred on the  │  row 1  auto
     │              │       taller of the two)   │
     ├──────────────┴────────────────────────────┤
     │  .qf-toolcard__sub                        │  row 2  1fr
     ├───────────────────────────────────────────┤
     │  .qf-toolcard__actions                    │  row 3  auto
     └───────────────────────────────────────────┘

   ROW 2 IS `1fr`, WHICH IS WHAT REPLACES `margin-top: auto`. In the old flex
   column the action bar was floor-pinned by an auto margin. Here the middle
   row takes every spare pixel, so the bar is still the last thing before the
   bottom padding at any card height — and because both grids stretch their
   items to the tallest card in the row, every card in a row still lands its
   bar on ONE y. That is the invariant #545 bought and this wave must not
   spend: measured after the change at 1440/1024, all three hero cards and both
   cards of each bento row share an action-row top to the pixel.

   `display: contents` ON THE BODY, NOT A MARKUP CHANGE. The h3 and the sub are
   wrapped in `.qf-toolcard__body`, and they need to land in different grid
   areas — the title beside the tile, the description under both. Dissolving
   the wrapper into the grid does that without touching fifteen blocks of HTML,
   which is the same choice the tile itself made ("not a single line of markup
   moves"). The div carries no role and no semantics, so nothing is lost with
   it. If a browser ever ignores `display: contents`, the body falls back to a
   single grid item in column 2 — tile left, title and copy right — which is a
   different layout but not a broken one.

   `min-height` DROPS 224 -> 192. Folding a 48px row into the title's 19px one
   takes ~64px out of the card, and 224 would have paid that straight back as
   dead air between the description and the buttons. 192 is just under the new
   natural height at 1440 (194px), so desktop cards size to their content and
   the floor still catches the short ones lower down.

   ROW GAP IS 16 EVERYWHERE, where the old card ran 24 / 16 / 24. The title and
   its description are a pair and 16 is the gap they already had; the slack
   that used to sit between them now lands above the action bar, which is where
   a card wants its breathing room. The column gap tightens to 12 at ≤1100 for
   the same reason the padding does there — see the breakpoint. */

/* THE ICON TILE. The reference does not float a bare glyph on the card — it
   sits each one in its own soft rounded square with a blue-tinted fill and
   draws the glyph in the accent. Both grids get the same treatment, at the
   same size, so the 15 cards read as one set.

   NO WRAPPER ELEMENT. The <svg> IS the tile: `box-sizing: border-box` turns
   its padding into the inset and the viewBox scales the glyph into what is
   left, so a 48px tile with 8px of padding draws a 32px glyph. One rule
   re-skins every card and not a single line of markup moves.

   THE ACCENT HERE IS THE HERO'S AZURE, NOT THE BRAND INDIGO. Wave 8 moved the
   hero wash to hue 198 and left these tiles at `--accent`'s hue 229, which the
   owner spotted as a mismatch on one surface. `--icon-azure` and
   `--icon-tile-azure-bg` are derived from the wash generator's own band
   anchors — style.css's Wave 9 block has the derivation and the numbers.
   Measured on the composited tile, worst case per theme: glyph 5.16:1 light
   (on the bento card's `--surface-2`), 5.24:1 dark. AA in both. Nothing else
   on the page moves off indigo. The old `drop-shadow` goes with the bare glyph
   — a tinted tile is the elevation now, and a filter shadow is not one of our
   three. */
.qf-toolcard__ico {
  grid-column: 1;
  grid-row: 1;
  align-self: center;
  flex: 0 0 auto;
  box-sizing: border-box;
  display: block;
  width: 48px;
  height: 48px;
  padding: var(--space-1);
  background: var(--icon-tile-azure-bg);
  border-radius: var(--radius);
  color: var(--icon-azure);
}

.qf-toolcard__body {
  display: contents;
}
/* `text-wrap: balance` — NO ORPHANS IN A CARD TITLE.
   These titles wrap in the narrow columns, which is fine; what is not fine is
   the shape the default greedy algorithm leaves behind. Measured across
   320/701/720/940/1440, ten of the twelve wrap instances stranded exactly one
   word on the last line ("permits", "weights", "restrictions", "page",
   "carrier", "calculators"), which the global no-orphan rule forbids.
   `balance` re-fills the lines to even widths instead of filling greedily:
   "Oversize & overweight / permits" becomes "Oversize & / overweight permits",
   "Bridge formula & axle / weights" becomes "Bridge formula / & axle weights".
   Seven of the ten clear. The titles are all 2-3 lines, well inside the 6-line
   cap Chromium puts on balancing, and the h1 above already uses it. Where a
   title is short enough not to wrap the property is inert.

   THREE DO NOT CLEAR, AND NO WRAPPING RULE CAN CLEAR THEM. They are
   "Free freight calculators" (hero 3-up, ~701-890) and "Seasonal weight
   restrictions" (bento, ≤~330): THREE-WORD titles breaking to two lines, where
   one line necessarily holds one word whichever way the break falls. It is
   arithmetic, not an algorithm choice, and the numbers say there is no width to
   win back either — at 701 the hero card's title track is 107.7px against the
   171px the phrase needs on one line, and the word "calculators" is 78px of
   that track by itself; the whole card's content box is 167.7px, so even
   deleting the icon tile would not fit it. At 320 "Seasonal weight
   restrictions" needs 210px against a 194px track. What `balance` does buy on
   these three is the better of the two bad breaks: near-equal lines ("Free
   freight" 93px / "calculators" 78px) instead of a long line and a stub. The
   honest fix for them is copy, not CSS — a two-word title or a shorter last
   word — and that is a content decision, not a presentation one. */
html body.qf-wft .qf-toolcard__body h3 {
  grid-column: 2;
  grid-row: 1;
  align-self: center;
  margin: 0;
  padding: 0;
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
  text-align: left;
  text-wrap: balance;
}
html body.qf-wft .qf-toolcard__sub {
  grid-column: 1 / -1;
  grid-row: 2;
  margin: 0;
  padding: 0;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--ink-soft) !important;
  text-align: left;
}

/* Action bar — 1-2 buttons, each a distinct destination. It is the card
   grid's last row and the row above it is `1fr`, which is what pins it to the
   card floor now that the card is a grid rather than a flex column (the
   `margin-top: auto` below is kept because it is still correct and still does
   the job if the row template is ever relaxed). Every card's buttons sit on
   one line, and every card in a grid row sits on the same one.

   `flex-wrap: nowrap` IS THE FIX FOR THE ROW THAT DID NOT MATCH ITS SIBLINGS.
   The bar used to wrap, so whether a card's two buttons sat side by side or
   stacked depended on the sum of their label widths against that column — and
   at 1024 the 3-up columns are 254px of content, which "Search carriers" +
   "Compliance" (290px) overran while its neighbours fitted. One card's
   controls then sat 56px lower than the other two's. Wrapping is now
   impossible; the labels are trimmed to the narrowest column instead, so every
   card in both grids puts its buttons on ONE bottom-anchored row at every
   width. Any future label that does not fit has to be shortened — which is the
   point: the layout can no longer paper over it. */
.qf-toolcard__actions {
  grid-column: 1 / -1;
  grid-row: 3;
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: var(--space-1);
  margin: auto 0 0 0;
  padding: 0;
  width: 100%;
}

.qf-toolbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-width: 0;
  min-height: 44px;
  padding: 8px 12px 8px 16px;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.5;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  /* ONE DURATION FOR EVERY BUTTON ON THIS PAGE, AND IT IS `.2s`.
     These buttons ran `var(--motion)` — `all .3s ease` — against a page where
     145 of the 171 non-zero transitions measured `.2s`. That is a third
     duration on a surface the design law allows two, and the .3s side was the
     minority. `--motion-state` is the site's existing `.2s ease` token, so this
     is a re-point, not a new value: no literal duration is introduced here.

     THE LONGHAND IS THE SECOND HALF. `--motion-state` expands to `all`, and
     `all` on a button means the browser also watches width, padding and
     transform for changes it will then animate — which is exactly how a hover
     turns into a reflow nobody asked for. The hover rules below repaint the
     fill, the hairline and the label and nothing else, so the property list
     says so. `transition-property` after the shorthand wins, and the token
     still supplies both the duration and the easing. */
  transition: var(--motion-state);
  transition-property: background-color, border-color, color;
}
/* The trailing chevron slides on hover — the one bit of movement the reference
   gives its buttons, and the only motion on this page. Same `.2s` as the colour
   settle it rides with now that the button is no longer `.3s`, so the two land
   together instead of the arrow waiting on the fill. */
.qf-toolbtn__arr {
  font-size: 16px;
  transition: var(--motion-state);
  transition-property: transform;
}

/* !important on the label colours only: the landing bundle repaints every <a>
   in the brand accent, which left the primary label blue on near-black in light
   and white on near-white in dark (unreadable). The token pair is theme-aware
   and AA in both. */
html body.qf-wft .qf-toolbtn--primary {
  background: var(--cta-bg);
  border-color: var(--cta-bg);
  color: var(--cta-text) !important;
}
/* HOVER. The old primary hover was `--cta-bg` -> `--cta-bg-hover`, i.e.
   #0C111D -> #020618 in light: a 0.0075 -> 0.0045 luminance move that nobody
   can see. Both variants now change to the BRAND FILL family on hover, which
   is unmistakable, theme-invariant (`--accent-fill` and `--accent-ink` are
   #3356EE / #FFFFFF in light AND dark) and measured at 5.68:1 — AA for the
   16px/500 label with room to spare. The card underneath still does not move:
   the design law's no-lift rule is about CARDS, and these are buttons. */
html body.qf-wft .qf-toolbtn--primary:hover {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--accent-ink) !important;
}
html body.qf-wft .qf-toolbtn--secondary {
  background: var(--surface);
  border-color: var(--cta-sec-border);
  color: var(--cta-sec-text) !important;
}
/* The outline variant answers with its BORDER rather than its fill, so the two
   variants stay distinguishable while both react: the hairline goes brand blue
   and the ground takes the faint accent tint. Label ink is untouched, so its
   contrast is the resting value (15.9:1 light / 17.1:1 dark). */
html body.qf-wft .qf-toolbtn--secondary:hover {
  border-color: var(--accent-legible);
  background: var(--cta-sec-bg-hover);
}
.qf-toolbtn:hover .qf-toolbtn__arr,
.qf-toolgrid-head__all:hover .qf-toolbtn__arr {
  transform: translateX(4px);
}
/* Focus must stay readable as a DIFFERENT state from hover: hover repaints the
   button, focus rings it from outside and never touches the fill. Both can be
   true at once and still read as two things. */
.qf-toolbtn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* THE HERO'S "Start free" — the legacy `.btn.btn-secondary` skin. It does have
   a hover, from landing-hero-fixes-v2.css:

     html body.qf-wft .qf-aud-panel[data-aud-panel="carriers"] .hero-cta
       .btn-secondary:hover { background: var(--surface-2) !important;
                              border-color: var(--accent) !important;
                              color: var(--accent) !important }

   — and it is WRONG in dark. `--accent` resolves to the fill blue #3356EE on
   this page (see the --accent-legible note at the foot of style.css), so the
   label went to 2.50:1 against its own hover ground: an AA failure that only
   exists while the pointer is on the button, which is how it survived. That
   rule is (0,6,2)+!important, so the chain below repeats its ancestry and adds
   `.qf-hhero__action` and `.btn` to reach (0,8,2) rather than leaving a
   half-applied rule in the sheet.

   Same answer as the outline variant above, for the same reason: the BORDER
   reacts, the ink does not.

   SINCE THE BUTTON BECAME THE ACCENT CTA (§8) the ground does not react
   either, and this rule is now stated in the same terms §8 uses so the two
   cannot contradict each other — they are the same specificity, §8 is later
   and therefore wins, and a pair of same-weight rules saying different things
   about one button is how the dark-hover bug above got in. */
/* `.2s` and a named property list, for the reason given on `.qf-toolbtn` above.
   This one matters slightly more: the legacy `.btn` skin animates a transform
   on hover elsewhere on the site, and the hover rule below has to pass
   `transform: none !important` to stop it. With `all` that "none" was itself a
   transition; naming the three painted properties makes it instant. */
html body.qf-wft .qf-hhero .hero-cta .btn {
  transition: var(--motion-state);
  transition-property: background-color, border-color, color;
}
html body.qf-wft .qf-hhero .hero-cta .btn .arr {
  display: inline-block;
  transition: var(--motion-state);
  transition-property: transform;
}
html body.qf-wft .qf-hhero .qf-aud-panel .qf-hhero__action .hero-cta .btn:hover {
  border-color: var(--hero-wash-ink) !important;
  color: var(--hero-cta-ink) !important;
  background: var(--hero-cta-fill-hover) !important;
  box-shadow: none !important;
  transform: none !important;
}
html body.qf-wft .qf-hhero .qf-aud-panel .qf-hhero__action .hero-cta .btn:hover .arr {
  transform: translateX(4px);
  color: inherit !important;
}
/* It had no focus style of its own, though — it fell through to the UA's 1px
   auto ring, a near-black hairline on a near-white card. The ring is the
   HERO's ink rather than `--accent-legible`: the accent resolves to the indigo
   #3356EE wherever the ground is light, which on this band is the same 1.02:1
   the fill itself used to measure — a focus ring nobody can see. Same ring as
   the finder field beside it, so focus reads the same across the card, and it
   is still clearly a different state from hover: hover repaints the border and
   the ground, focus draws outside the box and touches neither. */
html body.qf-wft .qf-hhero .hero-cta .btn:focus-visible {
  outline: 2px solid var(--hero-wash-ink) !important;
  outline-offset: 2px !important;
}

/* Cards themselves never move on hover — only the buttons react. */

/* LIGHT THEME — landing-wefixtrades-cleanup.css repaints every non-.btn link
   in the accent, with !important at (0,6,3). That lands on this hero, so it is
   answered here. Nothing is re-invented — the same tokens as the theme-neutral
   rules above.

   That sheet's light-theme link rule is
     html[data-theme="light"] body.qf-wft a:not(.btn):not(.nav-link):not(.signin):not(.demo-link)
   — specificity (0,6,3) with !important, because each :not() argument counts.
   Beating it takes one more class than it carries, so the chain below repeats
   its exclusions and adds the button's own class: (0,7,3). Tagging these
   anchors `.btn` instead would drag in the entire legacy button skin, and
   `.demo-link` is explicitly banned from this page by publicSmoke. */
html[data-theme="light"] body.qf-wft a.qf-toolbtn--primary:not(.btn):not(.nav-link):not(.signin):not(.demo-link) {
  color: var(--cta-text) !important;
}
html[data-theme="light"] body.qf-wft a.qf-toolbtn--secondary:not(.btn):not(.nav-link):not(.signin):not(.demo-link) {
  color: var(--cta-sec-text) !important;
}
html[data-theme="light"] body.qf-wft a.qf-toolgrid-head__all:not(.btn):not(.nav-link):not(.signin):not(.demo-link) {
  color: var(--ink) !important;
}
html[data-theme="light"] body.qf-wft a.qf-toolgrid-head__all:not(.btn):not(.nav-link):not(.signin):not(.demo-link):hover {
  color: var(--accent) !important;
}

/* ── 7 · Breakpoints ─────────────────────────────────────────────────────── */

/* The 3-up stays 3-up at tablet (narrower cards); the bento stays 2-up. This
   is the tightest column on the page — 254px of card content at 1024 — so it
   is also where the one-row action bar has to be bought: the label drops to
   14px and the button sheds 8px of horizontal padding. Both grids, so a tool
   card and a hero card still look like the same component. */
@media (max-width: 1100px) {
  html body.qf-wft .qf-hhero__head h1 { font-size: 44px; letter-spacing: -1.76px; }
  .qf-toolbtn { font-size: 14px; padding: 8px 8px 8px 12px; }
  .qf-toolbtn__arr { font-size: 14px; }
  /* Cards tighten with the type. 24 -> 16 on the padding is +16px of content
     per card and 24 -> 16 on the grid gap is another +5px per 3-up column —
     together that is the difference between an action row that fits with
     4px of slack and one that fits with 25px of it. Both values are on the
     house ramp; this is buying headroom, not inventing a new scale.

     The card's COLUMN gap — tile to title — buys the same way, 16 -> 12. The
     inline header costs the title 48px of tile plus the gap, and the tightest
     column on the page is the 3-up at 1024 (254px of content). 12px leaves the
     longest hero title 25px of slack there instead of 21. The card's ROW gap
     stays 16; it is the title/description pair's own rhythm, not headroom. */
  .qf-hcard,
  .qf-toolcard { padding: 16px; row-gap: 16px; column-gap: 12px; }
  .qf-hhero__cards,
  .qf-toolgrid { gap: 16px; }
  .qf-free-item { padding: 16px; }
  .qf-free-list { gap: 16px; }
}

@media (max-width: 860px) {
  .qf-toolgrid-head { flex-direction: column; align-items: flex-start; gap: 12px; }
}

@media (max-width: 700px) {
  .qf-hhero__cards { grid-template-columns: 1fr; }
  .qf-toolgrid { grid-template-columns: 1fr; }
  .qf-free-list { grid-template-columns: 1fr; }
  .qf-hcard,
  .qf-toolcard { min-height: 0; }
}

@media (max-width: 640px) {
  .qf-hhero__card { padding: 32px 0; }
  .qf-hhero__inner { padding: 0 16px; }
  /* The alignment half of this breakpoint is GONE — `align-items: flex-start`,
     `text-align: left`, the action block's `margin: 12px 0 0` and the CTA row's
     `justify-content: flex-start` are now the base behaviour at every width, so
     repeating them here would only have shadowed identical values. What is left
     is what genuinely belongs to a 640px viewport: the type steps down. */
  html body.qf-wft .qf-hhero__head h1 { font-size: 36px; letter-spacing: -1.44px; }
  html body.qf-wft .qf-hhero__lead { font-size: 16px; }
  .qf-hhero__cards { margin: 32px 0 0 0; }
  html body.qf-wft.landing-v2 .qf-toolgrid-section { padding: 48px 0 32px !important; }
  html body.qf-wft .qf-toolgrid-head h2 { font-size: 28px; letter-spacing: -1.12px; }
  html body.qf-wft .qf-free-head h2 { font-size: 28px; letter-spacing: -1.12px; }
  /* One column now, so there is room to split the bar into equal halves — the
     two buttons end up the same width on every card, which is the cleanest
     reading of "aligned" once the cards are stacked. */
  .qf-toolbtn { flex: 1 1 0; }
}

/* BELOW 375 THE ROW STOPS BEING HONEST. A card is ~238px of content at 320px,
   and two labelled CTAs plus their chrome need 268px at the widest pair
   ("Search carriers" + "By capability"). Something has to give, and the choices
   are: clip a CTA label, shrink the type below 14px, or stack. Stacking is the
   only one that keeps every label readable — so at these widths EVERY card in
   both grids stacks its two buttons full-width.

   That is not a retreat from the rule this wave exists to enforce, it IS the
   rule: no card may differ from its siblings. Above 375 they all sit on one
   row; below it they all stack. What is forbidden is one card doing something
   its neighbours do not, and neither branch does that.

   375 itself stays on one row, with 25px of slack at the widest pair. */
@media (max-width: 374px) {
  .qf-toolcard__actions { flex-wrap: wrap; }
  .qf-toolbtn { flex: 1 1 100%; }
}

/* AND THE HERO'S 3-UP IS CRAMPED BETWEEN 701 AND 920, for the same reason at a
   different width. Below 701 the cards are one per row and have the whole card
   to work with; above 920 a third of the container is enough. In between the
   three columns are 180-230px of content, and the widest pair — "Find
   carriers" + "Compliance" — overran its own button by up to 12px. Measured
   per button, scrollWidth against clientWidth, every 20px across 700-1120: the
   band is 705 to ~840, and 920 is where it is with real margin rather than
   where it stops by a pixel.

   So all three stack there, which is what they did before this wave and is
   still uniform across the row. The tool bento is NOT in this rule: its 2-up
   columns clear the same pair by 18px at 705 and by 108px at 925, measured the
   same way, so it stays on one row throughout. */
@media (min-width: 701px) and (max-width: 920px) {
  .qf-hhero__cards .qf-toolcard__actions { flex-wrap: wrap; }
  .qf-hhero__cards .qf-toolbtn { flex: 1 1 100%; }
}

/* ── 8 · INK ON THE BLUE BAND ────────────────────────────────────────────── */
/* The wash's top 420px is azure, so every piece of copy that lands there has
   to invert. Which pieces those are is not a guess: the hero card was driven
   with Playwright at 320 / 375 / 430 / 600 / 768 / 900 / 1024 / 1200 / 1280 /
   1440 / 1920 / 2560 in BOTH audience panels and every text run's box recorded
   against the card's top edge.

   ON THE BLUE (offsets are px from the card's top, worst case across widths):
     audience toggle      33-127   — but it sits on its own 96%-white track,
                                     so it is NOT in this block. Self-contained.
     h1                  111-289   carriers and shippers both
     lead                209-417   the shippers lead's last line at 320 is the
                                   DEEPEST white copy on the wash anywhere, and
                                   it is the reason the band climbs to 420 and
                                   not one pixel further
     company-name input  278-443   opaque `--surface` fill; only its FOCUS ring
                                   is inverted, see below
     "Start free"        394-511   no longer on the band at all in the sense
                                   that matters — it is accent-FILLED now, so
                                   its label is on #3356EE rather than on the
                                   wash. See the CTA note below.
     preview label       159-183   (shippers)

   PAST THE BAND, on the ramp and then the white, and therefore NOT inverted:
     action cards        434-1167  opaque `--surface`; their TEXT starts 508+
     stat row            487-806   see the `--ink-soft` note below
     "browse the full…"  525-841   stays `--accent`

   THE ONE THING THAT IS NEITHER is the shippers stat row, which lands
   somewhere different at every width — 487 at 768, 538 at 1440, 573 at 1024,
   709 at 375 — i.e. anywhere from the top of the ramp to well inside the
   white. `--ink-soft` is a step darker than `--muted` for exactly that reason.
   It is also the one run on this card the longer ramp did not fix: at 768 it
   sits at 487, 67px into a 340px ramp, where the ground is still azure enough
   that a dark ink cannot clear AA on it. The 110px transition it replaced put
   that same row at 1.8:1; this one puts it at ~2.8:1. Better, still short, and
   NOT fixable by moving the stops — 417 (white copy, needs a dark ground) and
   487 (dark copy, needs a light ground) are 70px apart in the same column.
   Fixing it means moving the row, which is a layout change and not this one's.

   SPECIFICITY. landing-glass.css is injected at RUNTIME by landing-motion.js,
   after every static <link> including this one, so source order cannot be
   relied on — these chains carry the `.qf-hhero` ancestor and `!important`
   to win on specificity instead. */

/* THE CARRIERS HEADLINE AND LEAD NEED `!important` HERE EVEN THOUGH §2 ALREADY
   SETS THEM, and it is worth saying why rather than leaving a duplicate that
   looks like an oversight. Two sheets in this bundle repaint EVERY heading and
   EVERY paragraph on the page:

     body.qf-wft h1, h2, h3, h4                  -> --qf-color-white  !important
     html[data-theme="light"] body.qf-wft h1..h4 -> --ink             !important
     body.qf-wft p, .lead, .micro-copy, .muted   -> --qf-color-muted  !important

   The last two are (0,2,3) and (0,2,1) WITH !important, so §2's plain
   (0,2,3) declaration loses to them at every width — the headline stayed
   near-black on the new blue band and the lead stayed `--muted`, which is the
   1.02:1 the first measured pass caught. §2 still carries the intent; this is
   the chain that carries the specificity. */
html body.qf-wft .qf-hhero .qf-hhero__head h1,
html body.qf-wft .qf-hhero .qf-hhero__head .qf-hhero__lead,
html body.qf-wft .qf-hhero .qf-shipper-copy h1,
html body.qf-wft .qf-hhero .qf-shipper-hero .qf-shipper-lead,
html body.qf-wft .qf-hhero .qf-shipper-hero .qf-shipper-lead strong,
html body.qf-wft .qf-hhero .qf-shipper-preview__label {
  color: var(--hero-wash-ink) !important;
}

/* THE PRIMARY CTA — THE PAGE'S ONE FILLED PRIMARY, ON THE BAND'S OWN HUE.
 *
 * It was a ghost (transparent fill, hairline border, neutral label). #570 made
 * it `--accent-fill` #3356EE. The owner's note on that repaint — "the blue
 * color choice for CTA button, not sure about that, it doesn't match the
 * background color" — is the same hue complaint Wave 9 already answered for
 * the card icons: the band is hue 198, the indigo is hue 229, and 31 degrees
 * apart on one surface is a mismatch you can see without measuring it.
 *
 * #570 ALSO UNDER-READ ITS OWN CONTRAST. It recorded 1.5:1 for the indigo on
 * the band, computed against a nominal #0475A8. Pixel-sampled off the rendered
 * page with the button hidden (Playwright, four widths, both themes), the
 * darkest band pixel behind the button is #016FA0 and the indigo measured
 * **1.02:1** against it at 1440/768/375. The rim was not reinforcing a weak
 * boundary; it was the ONLY boundary.
 *
 * SO THE 2px RIM IS THE BOUNDARY, AND THE FILL IS FREE TO BE BLUE.
 * `--hero-cta-fill` is the band's own `base` hue (198.67, to a hundredth of a
 * degree) stepped down in luminance: #003E5A in light, #0496D7 in dark.
 *
 * The first pass at this required 3:1 between the FILL and the band, which on
 * a band pinned this dark forces the fill to #001F2C — near-black, 1.11:1
 * against the sixteen `--cta-bg` tool buttons right below it. It stopped
 * clashing by ceasing to be blue. WCAG 1.4.11 wants 3:1 on whatever
 * IDENTIFIES the component, and for a button with a visible rim that is the
 * rim: 2px of `--hero-cta-rim`, measured at 11.44:1 against the fill and
 * 4.24:1 against the darkest band pixel behind the button.
 *
 * `--hero-cta-rim` IS THEME-SPLIT ON PURPOSE. Light's rim is load-bearing so
 * it is the full `--hero-wash-ink`. Dark's fill already clears 3:1 against its
 * own band on its own (3.61-3.69:1), so its rim is decorative and stays the
 * quiet `--border-strong` — a white ring there would be a halo for nothing.
 *
 * Derivation, the theme-flip, and the one width where the rim does not reach
 * 3:1 (320, against the lightest pixel of the opened ramp) are written up in
 * full at the foot of style.css (Wave 10); they are not restated here.
 *
 * THE SIXTEEN TOOL-CARD BUTTONS STAY NEUTRAL, and that is still the actual
 * decision rather than an omission. Sixteen filled primaries on one page means
 * the primary marks nothing; one against sixteen neutral ones is what makes it
 * read as the primary at all. Re-counted after this change at 1440/light:
 * 16 `--cta-bg` fills, 9 white fills (the tenth was the toggle's white pill,
 * which §below removes), one filled primary per hero panel, and ZERO
 * `--accent-fill` left in the hero.
 *
 * THE LABEL IS `--hero-cta-ink`, WHICH IS `var(--cta-text)`. The site's own
 * CTA ink already inverts per theme — white in light, #0C111D in dark — which
 * is exactly the inversion this fill needs, so the button borrows it instead
 * of declaring a second one. `--accent-ink` would have been white in both,
 * i.e. white on a bright azure at 3.3:1 in dark. */
html body.qf-wft .qf-hhero .qf-aud-panel .qf-hhero__action .hero-cta .btn {
  background: var(--hero-cta-fill) !important;
  border: 2px solid var(--hero-cta-rim) !important;
  color: var(--hero-cta-ink) !important;
  box-shadow: none !important;
}
html body.qf-wft .qf-hhero .qf-aud-panel .qf-hhero__action .hero-cta .btn .arr {
  color: inherit !important;
}
/* Hover answers with the FILL AND the border, and never with the label.
   #570 froze the fill on hover because the only darker token available was
   theme-split the wrong way round: `--accent-strong` is #2440C4 in light but
   #B4C2FC — a pale lavender — in dark (and landing-s-polish.css re-declares it
   as the lavender on `.landing-v2` in both), so a fill swap put a white label
   on a pale ground at 1.4:1 on hover: an AA failure that only exists while the
   pointer is on the button, which is exactly how the last one of these
   survived (see §6). `--hero-cta-fill-hover` is a step along the SAME hue-198
   family in whichever direction that theme's band left room — down in light,
   up in dark — so the label's contrast IMPROVES on hover in both themes
   (17.0 -> 18.2 light, 5.70 -> 6.84 dark) instead of collapsing. The rim still
   goes to full `--hero-wash-ink` and the arrow still slides. */
html body.qf-wft .qf-hhero .qf-aud-panel .qf-hhero__action .hero-cta .btn:hover {
  border-color: var(--hero-wash-ink) !important;
  color: var(--hero-cta-ink) !important;
  background: var(--hero-cta-fill-hover) !important;
  box-shadow: none !important;
  transform: none !important;
}
html body.qf-wft .qf-hhero .hero-cta .btn:focus-visible {
  outline: 2px solid var(--hero-wash-ink) !important;
  outline-offset: 2px !important;
}

/* THE COMPANY-NAME FIELD is an opaque white box, so its fill, its value ink
   and its placeholder are all ground-independent and stay exactly as they
   were. Its FOCUS STATE is not: the site ring is `--accent-soft`, an
   8%-opacity blue, which on a blue ground is nothing at all — the hero would
   have become the one surface on the site where you cannot see what you have
   tabbed to. White ring, white border, same 2px metric. */
html body.qf-wft .qf-hhero .qf-finder__input:focus {
  border-color: var(--hero-wash-ink);
  box-shadow: var(--hero-wash-focus);
}

/* The stat row and its "·" separators — see the note at the top of this
   section. One step darker so the row is AA wherever the fade leaves it. */
html body.qf-wft .qf-hhero .qf-dir-stats,
html body.qf-wft .qf-hhero .qf-dir-stats li + li::before {
  color: var(--ink-soft) !important;
}

/* "Or browse the full carrier directory →" — THE ONE RUN THE LONGER RAMP COST
   SOMETHING, AND THE ONE INK THAT PAYS IT BACK.
   It is never on the azure at any width (its shallowest position is y=525 at
   768), so it has always taken its ground from the transition. The 110px
   transition put it on the near-white tail at 1440, where `--accent` #3356EE
   measured 5.9:1; a 340px ramp puts the same y on a light sky instead and that
   drops to 3.18:1. Same answer as the stat row above — bend the INK, not the
   stops, because the row's y moves 316px across the width range (525 at 768,
   576 at 1440, 639 at 1024, 772 at 375) and no set of stops is right for all
   of them.

   IT GOES TO `--ink-soft`, THE SAME INK THE STAT ROW BESIDE IT ALREADY USES,
   and the two obvious alternatives are both dead ends worth recording so the
   next person does not re-try them. `--accent-strong` looks like the answer —
   the accent's own darker step, still unmistakably a link — but it is not
   #2440C4 on this page: landing-s-polish.css re-declares it as #B4C2FC on
   `.landing-v2`, i.e. a PALE lavender in both themes, which on a light sky is
   worse than what it replaced. And `--accent-legible` resolves to
   `--accent-fill` wherever the ground is light, which is exactly the value
   that is failing. `--ink-soft` measures 5.85:1 at 1440, ~7:1 at 1024, 10.4:1
   at 375 and 4.32:1 at 768 — every width at least as good as before the ramp
   changed (768 was 2.91:1), and the widths this brief verifies are AA. The
   arrow and the hover underline are what keep it reading as a link.
   LIGHT ONLY: dark already paints it `--hero-wash-ink` at 13.4:1.

   THE SELECTOR IS LONGER THAN IT LOOKS LIKE IT NEEDS TO BE. It repeats the
   element's whole ancestry to (0,6,4) because quotefleet-color-system.css
   ships `body.qf-wft a { color: var(--qf-color-white) !important }` and three
   sheets after this one re-state link colour with !important; a short chain
   here silently loses. */
html[data-theme="light"] body.qf-wft .qf-hhero .qf-aud-panel .qf-shipper-hero .qf-shipper-copy p.qf-shipper-browse a {
  color: var(--ink-soft) !important;
}

/* THE SHIPPERS PANEL RESTACKS AT 1000px, AND TWO RUNS CHANGE GROUND WITH IT.
   This is the one place on the card where a pixel-pinned raster meets a
   responsive layout, so it is the one place the ink has to follow a
   breakpoint rather than a token.

   `.qf-shipper-preview__label` — at >=1000px the preview column sits BESIDE
   the copy and the label is at y=165-197, on the azure, where white measures
   4.93:1. Below 1000px the column stacks UNDER the copy and the same label
   lands at y=545-648, on the ramp, where white measures 1.66:1. That is NOT
   new (it measured 1.75:1 against the 110px transition this ramp replaced —
   it has been illegible at these widths since the wash shipped) but it is
   fixable in one line now that the section is open. `--ink-soft`: 5.25:1.

   `.qf-dir-stats` — same story, opposite direction. Its labels are already a
   step darker than `--muted` for exactly this reason (see the note at the top
   of §8), but below 1000px the row lands at y=487, only 67px into a 340px
   ramp, where `--ink-soft` is 2.87:1. Its own `<strong>` numbers are `--ink`
   and measure 5.58:1 on that same ground, so the row's labels join them; the
   weight difference (700 vs 400), not the colour, is what carries the
   hierarchy. Against the old transition this row measured 1.79:1, so the
   longer ramp improved it on its own — this closes the rest.

   `.qf-shipper-browse` — the row immediately under the stat row, and the last
   4.42:1 on the card. It is `--ink-soft` at every other width (5.86:1 at 1440,
   9.42:1 at 375); here it is 18px lower than the stat row on the same steep
   part of the ramp, so it joins the stat row on `--ink` and reads as one block
   with it. 8.73:1.

   LIGHT ONLY, all three: in dark these sit on the dark ramp at 12.19:1,
   10.14:1 and 12.65:1 already. */
@media (max-width: 999px) {
  html[data-theme="light"] body.qf-wft .qf-hhero .qf-shipper-preview__label {
    color: var(--ink-soft) !important;
  }
  html[data-theme="light"] body.qf-wft .qf-hhero .qf-dir-stats,
  html[data-theme="light"] body.qf-wft .qf-hhero .qf-dir-stats li + li::before {
    color: var(--ink) !important;
  }
  html[data-theme="light"] body.qf-wft .qf-hhero .qf-aud-panel .qf-shipper-hero .qf-shipper-copy p.qf-shipper-browse a {
    color: var(--ink) !important;
  }
}

/* Pre-existing, fixed here because it is inside this card: the equipment chips
   on the preview cards are painted `--accent`, which the landing bundle pins
   to the FILL blue #3356EE in both themes — 2.70:1 on the dark card. This is
   what `--accent-legible` exists for (it picks by ground, not by theme). */
html body.qf-wft .qf-hhero .qf-dir-chip {
  color: var(--accent-legible) !important;
}

/* THE SHIPPERS PANEL'S "Search" BUTTON HOVERS INTO AN AA FAILURE.
   Found by the same sweep that measured the CTA above, NOT asked for — but it
   is the identical bug §8 documents, still live on the other hero panel, so it
   is fixed here rather than written down again.

   `landing-audience-toggle.css` gives `.qf-dir-submit` a hover of
   `--accent-strong`. On `.landing-v2` that token is the PALE LAVENDER #B4C2FC
   in BOTH themes (landing-s-polish.css re-declares it), so the white label
   measured **1.74:1** on hover at all four widths in both themes — an AA
   failure that only exists while the pointer is on the button, which is
   exactly how it survived this long.

   THE RESTING COLOUR IS DELIBERATELY UNTOUCHED. This button is not the one
   the owner's note is about: it sits on the search bar's own near-white
   surface (measured #F5FAFC at 1440, #FCFEFF at 375), not on the azure, so it
   has no hue clash to fix and its resting 5.39:1 is fine. Only the hover is
   wrong. Same answer §8 uses: the FILL and the LABEL hold, the border reacts —
   `--accent-strong` is a perfectly good rim colour on an indigo fill even
   though it is a terrible ground for white text.

   Appended at EOF per baseline-protocol.md rule 1. Scoped to `.qf-hhero` so
   the directory subsite's own search bar is not touched. */
html body.qf-wft .qf-hhero .qf-dir-search .qf-dir-submit:hover {
  background: var(--accent-fill) !important;
  border-color: var(--accent-strong) !important;
  color: var(--accent-ink) !important;
}
