/*
Theme Name:  SellHouseFaster
Theme URI:   https://sellhousefaster.co.uk/
Author:      Built for Khawar Nasir
Description: Conversion-focused WordPress theme for a Greater Manchester cash house-buying business. System font stack only, zero external requests, works fully with JavaScript disabled.
Version:     2.0.0
Requires at least: 6.0
Tested up to: 6.8
Requires PHP: 7.4
License:     GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: sellhousefaster
Tags:        business, custom-logo, custom-menu, blog, accessibility-ready
*/

/* ==========================================================================
   1. TOKENS
   Every colour, size and space on this page resolves to something below.
   ========================================================================== */

:root {

  /* --- Brand colour ---------------------------------------------------- */
  --ink:            #0B2436;   /* headings, header, footer */
  --primary:        #00BFA6;   /* bands, accents, icon strokes */
  --hover:          #00806F;   /* interactive states */
  --surface:        #F5F9F8;   /* alternating section backgrounds */
  --body:           #5F6B70;   /* body copy */
  --white:          #FFFFFF;

  /* The conversion colour, and the only place orange appears.
     The brand orange #FF5A1F measures 3.12:1 against white — legal only as
     large bold text, with a 4% margin, and illegal in the mobile call bar.
     This is the same hue (15.9deg vs 15.8deg) darkened until white labels
     clear AA at any size. Measured: white on #D53A02 = 4.73:1.
     To ship the brighter brand orange instead, change this one line — but
     then every CTA label must be >= 18.66px bold, without exception. */
  --cta:            #D53A02;
  --cta-hover:      #B33002;

  /* --- Derived neutrals ------------------------------------------------ */
  --line:           #DCE6E4;              /* decorative hairlines: 1.27:1, non-UI */
  /* Form controls need a 3:1 boundary under SC 1.4.11, so they get their own,
     darker line. Measured: #7E9096 on white = 3.33:1. */
  --field-line:     #7E9096;
  --line-dark:      rgba(255,255,255,.16); /* hairlines on ink */
  --ink-06:         rgba(11,36,54,.06);
  --ink-12:         rgba(11,36,54,.12);
  --primary-12:     rgba(0,191,166,.12);
  --primary-28:     rgba(0,191,166,.28);
  --on-ink:         rgba(255,255,255,.82); /* body copy on ink: 12.6:1 */
  --on-ink-quiet:   rgba(255,255,255,.62); /* meta on ink: 7.4:1 */
  --warn:           #8A5300;               /* placeholder flags: 4.9:1 on white */
  --warn-bg:        #FFF6E5;
  --error:          #B3261E;

  /* --- Type: major third, 1.25, base 17px ------------------------------ */
  --font: Arial, "Helvetica Neue", Helvetica, sans-serif;

  --t--1: 0.85rem;     /* 13.6 — the floor. Nothing on this site goes smaller. */
  --t-0:  1.0625rem;   /* 17   */
  --t-1:  1.328rem;    /* 21.25 */
  --t-2:  1.66rem;     /* 26.56 */
  --t-3:  2.076rem;    /* 33.21 */
  --t-4:  2.594rem;    /* 41.51 */
  --t-5:  3.243rem;    /* 51.89 */
  --t-6:  4.054rem;    /* 64.86 */

  /* Tracking. The spread between display and label is where the character
     of this page lives — Arial set at defaults reads like a memo. */
  --track-display: -0.03em;
  --track-h2:      -0.022em;
  --track-h3:      -0.015em;
  --track-tight:   -0.005em;
  --track-label:    0.14em;

  --measure: 62ch;

  /* --- Space: 4px base ------------------------------------------------- */
  --s-1:   0.25rem;  /*   4 */
  --s-2:   0.5rem;   /*   8 */
  --s-3:   0.75rem;  /*  12 */
  --s-4:   1rem;     /*  16 */
  --s-5:   1.5rem;   /*  24 */
  --s-6:   2rem;     /*  32 */
  --s-7:   3rem;     /*  48 */
  --s-8:   4rem;     /*  64 */
  --s-9:   6rem;     /*  96 */
  --s-10:  8rem;     /* 128 */

  /* --- Layout ---------------------------------------------------------- */
  --wide:    1200px;
  --narrow:  680px;
  --gutter:  clamp(var(--s-4), 4vw, var(--s-6));
  --band-y:  clamp(var(--s-7), 7vw, var(--s-9));

  /* --- Shape ----------------------------------------------------------- */
  --r-sm:  6px;
  --r-md:  10px;
  --r-lg:  16px;
  --r-pill: 999px;

  --shadow-1: 0 1px 2px rgba(11,36,54,.05), 0 2px 6px rgba(11,36,54,.04);
  --shadow-2: 0 4px 10px rgba(11,36,54,.06), 0 14px 34px rgba(11,36,54,.08);
  --shadow-3: 0 10px 24px rgba(11,36,54,.10), 0 34px 68px rgba(11,36,54,.12);

  --ease: 160ms cubic-bezier(.4, 0, .2, 1);

  /* Height reserved for the fixed mobile call bar. */
  --callbar-h: 0px;
}

/* ==========================================================================
   2. BASE
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: var(--s-8);
}

body {
  margin: 0;
  background: var(--white);
  color: var(--body);
  font-family: var(--font);
  font-size: var(--t-0);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-wrap: break-word;
  padding-bottom: var(--callbar-h);
}

img, svg, video, iframe { max-width: 100%; height: auto; }
img { display: block; }

a { color: var(--hover); text-underline-offset: 3px; text-decoration-thickness: 1px; }
a:hover { color: var(--ink); }

hr { border: 0; border-top: 1px solid var(--line); margin: var(--s-6) 0; }

/* --- Focus -----------------------------------------------------------------
   Teal is only 2.33:1 on white, so it cannot carry a focus ring. Ink does
   (15.9:1). On ink-coloured surfaces the ring flips to white (also 15.9:1),
   so a visible ring is guaranteed on every background this theme uses.      */
:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}
.hero :focus-visible,
.shf-header :focus-visible,
.shf-footer :focus-visible,
.callbar :focus-visible,
.band--ink :focus-visible,
.shf-page-hero :focus-visible,
.shf-cta-band :focus-visible {
  outline-color: var(--white);
}

.shf-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 999;
  padding: var(--s-3) var(--s-5);
  background: var(--ink);
  color: var(--white);
  font-weight: 700;
  text-decoration: none;
}
.shf-skip-link:focus { left: var(--s-4); top: var(--s-4); color: var(--white); }

.screen-reader-text {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}
.screen-reader-text:focus {
  position: static !important;
  width: auto; height: auto; clip: auto; clip-path: none;
}

/* ==========================================================================
   3. TYPE
   ========================================================================== */

h1, h2, h3, h4, h5, h6 {
  margin: 0 0 var(--s-4);
  color: var(--ink);
  font-weight: 700;
}

h1 {
  font-size: clamp(var(--t-4), 7.2vw, var(--t-6));
  line-height: 0.98;
  letter-spacing: var(--track-display);
}
h2 {
  font-size: clamp(var(--t-2), 4.4vw, var(--t-4));
  line-height: 1.04;
  letter-spacing: var(--track-h2);
}
h3 { font-size: var(--t-2); line-height: 1.14; letter-spacing: var(--track-h3); }
h4 { font-size: var(--t-1); line-height: 1.22; letter-spacing: var(--track-tight); }
h5, h6 { font-size: var(--t-0); line-height: 1.3; letter-spacing: 0; }

p { margin: 0 0 var(--s-4); max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

strong, b { color: var(--ink); font-weight: 700; }

/* The counterweight to the display tracking. Only ever used on short labels. */
.label {
  display: block;
  margin-bottom: var(--s-3);
  font-size: var(--t--1);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--hover);
}
.label--on-ink { color: var(--primary); }

.lead {
  font-size: var(--t-1);
  line-height: 1.45;
  letter-spacing: var(--track-tight);
  max-width: 54ch;
}

.fine { font-size: var(--t--1); line-height: 1.5; }
.quiet { color: var(--body); }

/* Dates and figures line up in columns. */
.tnum { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

blockquote {
  margin: var(--s-6) 0;
  padding: var(--s-5) var(--s-6);
  border-left: 3px solid var(--primary);
  background: var(--surface);
  color: var(--ink);
  font-size: var(--t-1);
  line-height: 1.45;
  letter-spacing: var(--track-tight);
}
blockquote cite { display: block; margin-top: var(--s-3); font-size: var(--t--1); font-style: normal; color: var(--body); letter-spacing: 0; }

code, kbd, samp, pre { font-family: ui-monospace, Consolas, "Courier New", monospace; font-size: .92em; }
code { padding: .12em .38em; background: var(--ink-06); border-radius: var(--r-sm); color: var(--ink); }
pre { padding: var(--s-5); background: var(--ink); color: #E6F2F0; border-radius: var(--r-md); overflow-x: auto; line-height: 1.5; }
pre code { background: none; color: inherit; padding: 0; }

/* ==========================================================================
   4. LAYOUT
   ========================================================================== */

.wrap {
  width: 100%;
  max-width: var(--wide);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: var(--narrow); }

.band { padding-block: var(--band-y); }
.band--tight { padding-block: clamp(var(--s-6), 4vw, var(--s-7)); }

/* #F5F9F8 is fixed by the design system, but it measures 1.06:1 against white
   — it physically cannot carry section alternation on its own. The token stays
   as specified; the structural work is done by the ink and teal fields, and a
   hairline marks the seam so the change of surface is at least legible. */
.band--surface { background: var(--surface); border-block: 1px solid var(--line); }

/* The brand primary as an actual field. Measured: ink on #00BFA6 = 6.82:1.
   White text is forbidden here (2.33:1) — everything on teal is ink. */
.band--brand { background: var(--primary); color: var(--ink); }
.band--brand h1, .band--brand h2, .band--brand h3 { color: var(--ink); }
.band--brand .label { color: var(--ink); }
.band--brand a { color: var(--ink); }
.band--brand :focus-visible { outline-color: var(--ink); }

/* Safari below 15.4 does not support :focus-visible. With the old
   `input:focus { outline: none }` gone, those browsers would otherwise get no
   ring at all rather than a bad one. Costs nothing in browsers that do. */
@supports not selector(:focus-visible) {
  :focus { outline: 3px solid var(--ink); outline-offset: 3px; }
  .hero :focus, .shf-header :focus, .shf-footer :focus,
  .callbar :focus, .band--ink :focus, .shf-page-hero :focus { outline-color: var(--white); }
}
.band--ink { background: var(--ink); color: var(--on-ink); }
.band--ink h1, .band--ink h2, .band--ink h3, .band--ink h4 { color: var(--white); }

/* Two sections carry a heading and one row of cards and nothing else. At
   --band-y they were 35% padding by height, 280px of content in a 507px
   band. Mobile already sat at the clamp floor, so only desktop moves.
   Lives here rather than beside its .band-head rules further down: equal
   specificity with .band means source order decides, and the region lint
   is right that a second .band* region is how a rule goes silently dead. */
.band--compact { padding-block: var(--s-7); }
@media (min-width: 1024px) {
  .band--compact { padding-block: var(--s-8); }
}

.band-head { max-width: 40ch; margin-bottom: var(--s-7); }
.band-head p { font-size: var(--t-1); line-height: 1.45; letter-spacing: var(--track-tight); }
/* Opt-in, and used exactly once on the site. The condition used to be written
   as "an eyebrow AND an intro"; its only user has since lost its eyebrow, so
   the wording is restated rather than left describing a case that no longer
   exists. What the note was protecting is unchanged: six identical hairlines
   down one page is a template signature, so this stays on the one band whose
   head introduces two columns of cards and therefore has a real boundary to
   mark. Anything else that wants it needs the same argument. */
.band-head--ruled::after {
  content: "";
  display: block;
  height: 2px;
  margin: var(--s-4) 0 0;
  background: currentColor;
  opacity: .16;
}


.grid { display: grid; gap: var(--s-5); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); }

/* Spacing utilities used by the blog templates. */
.shf-text-center { text-align: center; }
.shf-mb-0 { margin-bottom: 0; }
.shf-mb-4 { margin-bottom: var(--s-4); }
.shf-mt-3 { margin-top: var(--s-3); }
.shf-mt-4 { margin-top: var(--s-4); }
.shf-mt-5 { margin-top: var(--s-5); }
.shf-mt-6 { margin-top: var(--s-6); }
.shf-mt-7 { margin-top: var(--s-7); }
.shf-muted { color: var(--body); }

/* ==========================================================================
   5. BUTTONS
   Orange appears on exactly one element per view: the primary action.
   ========================================================================== */

.btn {
  --_bg: var(--ink);
  --_fg: var(--white);
  --_bg-hover: #16405A;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 48px;              /* > 44px touch target */
  padding: var(--s-3) var(--s-5);
  background: var(--_bg);
  color: var(--_fg);
  border: 2px solid transparent;
  border-radius: var(--r-md);
  font-family: var(--font);
  font-size: var(--t-0);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: var(--track-tight);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--ease), transform var(--ease), box-shadow var(--ease);
}
/* Small, fast, and only ever in answer to the reader. 1px of lift and a
   shadow reads as a button that is ready; anything larger reads as a
   button that wants something. --ease is 160ms, well inside the 250ms
   ceiling, and the whole thing is transform and shadow so it composites. */
.btn:hover, .btn:focus-visible { background: var(--_bg-hover); color: var(--_fg); }
.btn--cta:hover, .btn--cta:focus-visible,
.btn--quiet:hover, .btn--quiet:focus-visible {
  transform: translateY(-1px);
  box-shadow: var(--shadow-2);
}
.btn:active { transform: translateY(1px); box-shadow: none; }

/* THE conversion button. White on #D53A02 = 4.73:1. */
.btn--cta {
  --_bg: var(--cta);
  --_bg-hover: var(--cta-hover);
  --_fg: var(--white);
}

/* Teal fill must carry ink text: white on #00BFA6 is only 2.33:1. */
.btn--primary {
  --_bg: var(--primary);
  --_fg: var(--ink);
  --_bg-hover: #00D9BC;
}

.btn--quiet {
  --_bg: transparent;
  --_fg: var(--ink);
  --_bg-hover: var(--ink);
  border-color: var(--ink-12);
}
.btn--quiet:hover, .btn--quiet:focus-visible { color: var(--white); border-color: var(--ink); }

.btn--onink {
  --_bg: transparent;
  --_fg: var(--white);
  --_bg-hover: rgba(255,255,255,.14);
  border-color: rgba(255,255,255,.42);
}

.btn--lg { min-height: 60px; padding: var(--s-4) var(--s-6); font-size: var(--t-1); }
.btn--block { display: flex; width: 100%; }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.btn-row--center { justify-content: center; }

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  font-weight: 700;
  letter-spacing: var(--track-tight);
  color: var(--hover);
  text-decoration: none;
}
.link-arrow:hover { color: var(--ink); text-decoration: underline; }

/* ==========================================================================
   5b. THE CALL GLOW — REMOVED
   Three buttons — the hero call block, the header CTA and the fixed bar's CTA
   — carried a ::before halo: the button's own box, painted in --cta, blurred
   14px, faded in to .55 on hover and focus. It was the surviving half of a
   pulsing beacon whose @keyframes went several chunks ago, and what was left
   read as a shine on a page asking for a six-figure decision. Gone: the
   pseudo-element, the two stacking contexts that existed only to hold it
   behind its own button, and the reduced-motion override that only ever
   silenced it. Two tokens went with it — --cta-rgb, which existed so the
   halo's alpha could animate without a second hardcoded orange, and
   --ease-breathe, the curve it breathed on. Each had exactly one reference in
   the file: its own declaration.

   Nothing replaces it, because the replacement was already underneath: all
   three take .btn's plain darker fill on hover, #D53A02 -> #B33002 over --ease
   at 160ms. Measured on all three at rest, under a real pointer and under real
   keyboard focus before anything was deleted — see .work/probe/states.js —
   which is also how the halo was ruled out as the focus indicator. It was
   carrying focus on none of them: each keeps the 3px white ring its context
   (.hero, .shf-header, .callbar) already gives it, at 3px offset.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  /* The lift goes; the focus ring and the background change stay, because
     those are how the button says it is focused, not how it moves. */
  .btn:hover, .btn:focus-visible, .btn:active { transform: none; box-shadow: none; }
}

/* ==========================================================================
   6. PLACEHOLDER FLAG
   Anything not yet supplied by the client is marked on the front end so it
   cannot be shipped by accident. Disappears the moment real data is set.
   ========================================================================== */

.placeholder {
  position: relative;
  padding: var(--s-6);
  background: var(--white);
  border: 1px solid var(--line);
  border-top: 3px solid var(--warn);
  border-radius: var(--r-lg);
}
.placeholder__flag {
  display: inline-block;
  margin-bottom: var(--s-4);
  font-size: var(--t--1);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--warn);
}
.placeholder--inline {
  display: block;
  width: 100%;
  padding: var(--s-2) var(--s-3);
  background: var(--warn-bg);
  border: 1px solid var(--warn);
  border-radius: var(--r-sm);
  color: #4A3000;
  font-size: var(--t--1);
  font-weight: 600;
  line-height: 1.45;
  text-align: left;
}
/* 0,0,3,1 on purpose: `.callblock__note strong` sets white further down the
   file at identical specificity, and source order was silently winning. */
.placeholder.placeholder--inline strong { color: #4A3000; }

/* ==========================================================================
   7. HEADER
   ========================================================================== */

/* White header. The accent on "Faster" moves from --primary to --hover:
   #00BFA6 measures 2.33:1 on white and fails, #00806F measures 4.86:1. */
.shf-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--white);
  color: var(--ink);
  border-bottom: 1px solid var(--line);
}
.shf-header__bar {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  min-height: 64px;
  padding-block: var(--s-2);
}
/* The nav row.

   Stacking context, below the fixed top row's 100 and above everything in the
   page. It was added for a desktop dropdown that is gone, but it is not
   orphaned with it: the mobile menu is a :target panel that opens from this
   row, and without the context it paints in document order, behind the hero
   that follows it.

   The ground goes here and not on the .wrap inside it, so the colour runs the
   full viewport while the items stay on the content grid and the first block's
   left edge lines up with the logo above.

   --primary, not --ink. Navy put a dark band directly above a dark hero and
   the two met as one mass, with only the active block visible across the join.
   #00BFA6 against the hero is 6.82:1 where navy was 1:1. #00806F was the
   alternative and manages only 3.27:1 there; off-white is ruled out by the
   white row above, against which #F5F9F8 is no edge at all. The trade is that
   white labels are impossible on teal at 2.33:1, so the labels are navy and
   the active item inverts to a navy block.

   The top border is the navy hairline the panel rows use. Teal against white
   is 2.33:1, so without it the band has a hard lower edge and no upper one.
   --cta is nowhere near this component.

   Four stacked comment blocks had accumulated here, one per chunk, two of them
   describing a navy bar and three items. That is also what pushed this rule 42
   lines from .shf-header__bar and split the component into two regions. */
.shf-header__navrow {
  position: relative;
  z-index: 90;
  background: var(--primary);
  border-top: 1px solid rgba(11,36,54,.18);
}
.shf-header__navrow .shf-nav ul { justify-content: flex-start; padding-block: 0; }

.shf-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  margin-right: auto;
  min-height: 44px;
  color: var(--ink);
  text-decoration: none;
}
/* The logo box is reserved, not fitted. Both numbers are declared so the row
   is the right height on the very first paint and the image drops into a box
   that already exists -- the alternative is the header growing 46px the moment
   the file decodes, which is the layout shift Core Web Vitals measures. This
   is the first image on the site, so it is also the first chance to get that
   wrong.

   640x180 source, rendered at half. object-fit:contain rather than cover, so a
   file that arrives at the wrong ratio letterboxes inside the box instead of
   being cropped into or stretched -- wrong, but visibly wrong, and not
   dragging the header out of shape on nineteen pages.

   Nothing here renders until the custom_logo theme mod is set; header.php
   falls back to the text wordmark while it is empty. */
.shf-brand img {
  display: block;
  width: 95px;
  height: auto;
  max-height: 64px;
  object-fit: contain;
  object-position: left center;
}
.shf-brand__text {
  display: block;
  font-size: var(--t-1);
  font-weight: 700;
  line-height: 1;
  letter-spacing: var(--track-h3);
  color: var(--ink);
}
.shf-brand__text span { color: var(--hover); }

/* gap:0 and stretch, so the items touch and each one's hit area is the whole
   height of the bar rather than the height of its label. li is a flex box for
   the same reason: it hands the anchor a box to fill. */
.shf-nav ul { display: flex; align-items: stretch; gap: 0; list-style: none; margin: 0; padding: 0; }
.shf-nav li { display: flex; }
.shf-nav a {
  display: flex;
  align-items: center;
  min-height: 48px;
  /* 32px against a 48px bar. At 24 the blocks read as labels that happen to
     have a background; at 32 they read as blocks, which matters more here than
     usual because there are only three of them holding the row. */
  padding-inline: var(--s-6);
  color: var(--ink);
  /* --t-0 is 17px, which lands inside the 17-18px the reference reads at.
     --t--1 was 13.6px: the gap was wider than reported, not narrower. No raw
     value needed, and the mobile rows were already on this token, so the two
     now agree instead of the desktop bar being the smaller of the two.

     Weight stays 700. The stack is Arial and 700 IS its bold; asking for 800
     or 900 gets the same face back or a synthetic emboldening that reads as
     blur at this size. Presence here comes from the size, not from a heavier
     axis that does not exist without a web font -- and there are none. */
  font-size: var(--t-0);
  font-weight: 700;
  /* 0, not 0.01em. The positive tracking was set when these labels were
     13.6px, where it helps small bold text; at 17px it reads as loose. The
     mobile rows were already at 0, so the two now agree. */
  letter-spacing: 0;
  text-decoration: none;
}

/* Hover moves AWAY from the active state, on two axes at once.

   It was navy at 12% over the teal: the same hue as the active block, one step
   short of it. So a hovered item was a paler "you are here" and began to read
   as the current page — the two sat on one axis and differed only in how far
   along it they had got.

   Now the ground goes LIGHTER on hover, #00BFA6 -> #38CDBA, while the active
   block goes to solid navy. Opposite directions from the same start, and
   8.04:1 apart, so neither can be taken for the other without the other there
   to compare against. And the marker that actually carries the state is not
   the wash but a 3px navy rule along the bottom edge of the cell: an edge
   rather than a fill is a different KIND of signal, not a smaller helping of
   the same one, and it means the state is not colour alone.

   Numbers: the label on the hovered ground is 8.04:1, up from 5.58:1. The rule
   against that ground is 8.04:1, well past the 3:1 a non-text indicator needs.
   The wash against the un-hovered teal is only 1.18:1 — which is exactly why
   the rule is there and does the work.

   inset box-shadow, not border-bottom: a border adds 3px to a 48px row and
   would drag --shf-fixed-row with it. This paints inside the box and costs no
   layout. Focus is the third state and keeps its own: the wash plus a full 3px
   ring, no bottom rule, so hover, focus and active are three different things
   rather than three amounts of one thing.

   --cta stays out of the nav entirely; it is enquiry buttons only. */
.shf-nav a:hover,
.shf-nav a:focus-visible { background: rgba(255,255,255,.22); color: var(--ink); }
.shf-nav a:hover { box-shadow: inset 0 -3px 0 var(--ink); }
/* The current page inverts: navy block, white label. The block reads against
   the teal at 6.82:1 and the label against the block at 15.91:1, both better
   than anything --hover could give as a ground -- and that frees --hover for
   the hover state on the active item, which would otherwise have none. */
.shf-nav .current-menu-item > a,
.shf-nav .current_page_item > a { background: var(--ink); color: var(--white); }
/* The active item's treatment is unchanged. The bottom rule is cleared
   explicitly rather than left to be invisible: navy on a navy block shows
   nothing, but leaving it declared would make the active hover depend on a
   colour clash to look right. */
.shf-nav .current-menu-item > a:hover,
.shf-nav .current_page_item > a:hover { background: var(--hover); box-shadow: none; }
/* Inset so the ring is not clipped by the bar's own edge. A ring plus the wash
   means focus is never signalled by colour alone. */
.shf-nav a:focus-visible { outline: 3px solid var(--ink); outline-offset: -3px; }
/* Navy on a navy block is no ring at all. */
.shf-nav .current-menu-item > a:focus-visible,
.shf-nav .current_page_item > a:focus-visible { outline-color: var(--white); }


/* The header contact group: digits, a caption under them, and a compact CTA
   beside them.

   It was one 279x73 two-line navy block. That is a slab, not a lockup: the
   number lived inside a button, so the thing you read and the thing you press
   were the same object and neither had priority. Split, the digits read as the
   contact and the button is a small, separate action.

   Nothing moves on its own and nothing glows. */
.header-contact {
  display: flex;
  align-items: center;
  gap: var(--s-5);
}
.header-contact__phone {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  line-height: 1.15;
}
/* The caption sits outside the tel: link, so it is context rather than part of
   the thing being activated.

   Capped, and allowed to wrap. Unwrapped it measured 204px under a 166px
   number, so the block was bottom-heavy and the eye landed on the caption
   before the digits -- the inversion of what a phone lockup wants.

   10rem, not the 11 first tried: 11rem is 176px and the number measures 166,
   so the caption still overhung it by 10px and the brief's "can never
   out-measure the number" was not actually met. 160px is under it for this
   number and for any longer one, since a longer number only widens the row
   above. */
.header-contact__note {
  max-width: 10rem;
  font-size: var(--t--1);
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1.25;
  color: var(--body);
  text-align: right;
}


/* Plain text, not a button. --t-1 so the digits are the largest thing in the
   row after the wordmark; navy on white measures 15.9:1. */
/* min-height 44: the geometry probe measured this at 166x24 from 768 up, the
   first thing it found. It stopped being a button in chunk 12 and stopped
   being a 44px target with it. */
.header-call {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  gap: var(--s-2);
  color: var(--ink);
  font-size: var(--t-1);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  text-decoration: none;
  white-space: nowrap;
}
.header-call svg { color: var(--hover); flex: 0 0 auto; }
.header-call:hover, .header-call:focus-visible { color: var(--hover); }
.header-call:focus-visible { outline: 3px solid var(--hover); outline-offset: 3px; border-radius: var(--r-sm); }

/* Compact. The point of splitting was to stop the top right being a slab, so
   this stays smaller than any enquiry button in the page body. */
.header-offer {
  min-height: 44px;
  padding: var(--s-3) var(--s-4);
  font-size: var(--t--1);
  white-space: nowrap;
}

@media (prefers-reduced-motion: no-preference) {
  /* 160ms, inside the 250ms ceiling. The rule lives inside no-preference, so
     under reduce there is no transition at all rather than a quick one. */
  .shf-nav a { transition: background-color var(--ease), box-shadow var(--ease); }
}

/* The close control is the same object in the same place, so it takes the same
   box. It is display:none until :target matches -- see the swap below. */
.shf-nav-toggle,
.shf-nav-close {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  padding: 0;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  color: var(--ink);
  text-decoration: none;
  cursor: pointer;
}
.shf-nav-toggle__bars, .shf-nav-toggle__bars::before, .shf-nav-toggle__bars::after {
  display: block; width: 18px; height: 2px;
  background: currentColor; border-radius: 2px;
  transition: transform var(--ease), opacity var(--ease);
}
.shf-nav-toggle__bars { position: relative; }
.shf-nav-toggle__bars::before { content: ""; position: absolute; top: -6px; }
.shf-nav-toggle__bars::after  { content: ""; position: absolute; top:  6px; }
.shf-nav-toggle[aria-expanded="true"] .shf-nav-toggle__bars { background: transparent; }
.shf-nav-toggle[aria-expanded="true"] .shf-nav-toggle__bars::before { transform: translateY(6px) rotate(45deg); }
.shf-nav-toggle[aria-expanded="true"] .shf-nav-toggle__bars::after  { transform: translateY(-6px) rotate(-45deg); }
/* The same X, for the path where no script is running to set aria-expanded. */
.shf-nav-toggle__bars--x { background: transparent; }
.shf-nav-toggle__bars--x::before { transform: translateY(6px) rotate(45deg); }
.shf-nav-toggle__bars--x::after  { transform: translateY(-6px) rotate(-45deg); }

@media (max-width: 1023px), (hover: none) {
  .shf-nav-toggle { display: inline-flex; }
  /* Both are flex items in a row that competes for width, and without this
     they shrink: the toggle measured 23x44 at 600 once the tagline was added
     beside the logo. It only had a floor below 480 before, so the whole
     481-1023 band was unprotected and nothing had noticed. */
  .shf-nav-toggle, .shf-nav-close { flex: 0 0 auto; }

  /* The way out, on the JavaScript-off path.
     :target only matches when nav.js has not run -- the script preventDefaults
     the toggle and drives the panel from data-open, so the fragment is never
     set and neither of these two rules ever fires while it is running. With
     the script gone, the toggle is replaced in place by a control that has its
     own accessible name and an href that clears the fragment.
     Without :has() support neither rule matches: the toggle stays, the panel
     still opens, and closing it means following a link or reloading. That is
     the pre-existing behaviour, not a new trap, and it needs a browser with
     no :has() AND no JavaScript to reach it. */
  .shf-header:has(.shf-nav:target) .shf-nav-toggle { display: none; }
  .shf-header:has(.shf-nav:target) .shf-nav-close { display: inline-flex; }

  .shf-nav {
    position: absolute; left: 0; right: 0; top: 100%;
    background: var(--primary);
    border-top: 1px solid rgba(11,36,54,.18);
    box-shadow: var(--shadow-3);
    max-height: 0; overflow: hidden;
    /* visibility, not just max-height. A collapsed panel clipped to zero height
       still leaves its links in the tab order: tabbing off the hamburger landed
       on three invisible 360x52 rows, verified by focusing one and reading
       activeElement back. visibility:hidden takes them out of it. The 0s delay
       holds visibility until the collapse has finished, so closing still
       animates; opening flips it immediately. */
    visibility: hidden;
    transition: max-height 220ms ease, visibility 0s linear 220ms;
  }
  .shf-nav[data-open="true"], .shf-nav:target {
    max-height: 80vh;
    overflow-y: auto;
    visibility: visible;
    transition: max-height 220ms ease, visibility 0s;
  }
  /* The gutter moves from the list to the anchor, so each row is a full-width
     bar with its label on the page's own content edge rather than a label in a
     list that has been inset. Without this the active block would stop short of
     both sides and stop reading as a row. */
  .shf-nav ul { flex-direction: column; align-items: stretch; gap: 0; padding: 0; }
  .shf-nav li { width: 100%; display: block; }
  /* Same ground as the bar, so the panel is the bar unrolled rather than a
     second component. Dividers go navy for the same reason the labels did:
     a white hairline on teal is barely there. */
  .shf-nav a {
    min-height: 52px;
    padding-inline: var(--gutter);
    border-bottom: 1px solid rgba(11,36,54,.18);
    font-size: var(--t-0);
    letter-spacing: 0;
  }
}

@media (max-width: 480px) {
  /* The tagline stays. It was hidden here when three things competed for one
     row; the contact group has gone, so the only things left are the wordmark
     and the toggle, and this is the one line above the fold at 360 that says
     what the company does before the hero loads. */
  /* The contact group goes entirely below 480. The fixed call bar at the
     bottom of the screen already carries a captioned "Call now" on every
     template -- checked on all twenty-two URLs, not assumed -- so the header
     copy was the second phone affordance on the same screen, and at 360 it
     sat 8px from the wordmark at almost exactly the same width, so the two
     read as a pair of buttons rather than a mark and an action.

     That also retires the border/radius tweak proposed for this button last
     chunk: there is no button here to soften. */
  /* The number comes back at 360, but as the plain text link the desktop
     header uses -- NOT as a button. The fixed bar at the bottom of the screen
     carries an orange button reading "Call now"; this carries the digits
     themselves in navy with a teal handset and no chrome. The two do not read
     as the same control: one is the number, one is the action. That is the
     whole reason this is not the navy button that used to sit here.

     The note and the enquiry CTA stay hidden. The note is two wrapped lines
     and the CTA is a second button, and neither survives the arithmetic in
     the comment below. */
  /* The same orange CTA the desktop header carries -- same label, same
     colour, same destination -- not the digits and not a second design.

     --cta is reserved for buttons that start an enquiry and this is that
     button, pointing at the postcode form rather than at a phone line. The
     fixed bar at the bottom carries an orange "Call now", which is a phone
     action; measurements and the honest read on whether those two collide are
     in the report rather than asserted here.

     The number leaves the header at this width. It is still on the screen:
     the fixed bar carries it on all twenty-two live URLs, checked. */
  .header-contact { display: flex; gap: 0; }
  .header-contact__note,
  .header-call { display: none; }

  /* Quieter, because at 159px it was twice the mark's width in the loudest
     colour on the site, and the eye reached "Get your cash offer" before it
     registered whose site this is.

     The brief asked for --t--2 sizing. There is no --t--2: --t--1 is 13.6px
     and the token block calls it the floor -- "nothing on this site goes
     smaller" -- and this button is already on it. The only lever left is
     padding, --s-4 down to --s-3; the label is nineteen characters of existing
     copy this chunk may not rewrite. The measured result is not the 120px the
     brief predicted and the arithmetic is in the report. */
  .header-offer {
    display: inline-flex;
    padding-inline: var(--s-3);
  }

  /* No override here any more: the mark takes the base 95px at this width too.

     77px was set when the row held three things — mark, number, toggle — and
     the arithmetic was about fitting all of them. The number and the tagline
     have both gone since, so the row is the mark and a 44px toggle, and the
     measurement that mattered turned out to be a different one: at 77 the mark
     was 1.96x smaller than the orange button beside it, and the header read as
     a button with a logo next to it rather than a brand with an action next to
     it. 95px is the base value, not a new one. The ratio and the row height it
     costs are both re-measured in the report, not predicted. */

  /* Three things in the row became two. The toggle keeps its 44px floor and
     the brand now has the rest, but the truncation guard stays: a longer site
     name would otherwise paint out of its box the way this one did. */
  /* the 44px floor now lives in the 1023 block, where the toggle is shown */
  .shf-brand { min-width: 0; overflow: hidden; }
  .shf-brand > span { min-width: 0; overflow: hidden; }
  .shf-brand__text {
    font-size: var(--t-0);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* ==========================================================================
   8. MOBILE CALL BAR
   ========================================================================== */

.callbar {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 120;
  padding: var(--s-2) var(--s-3) calc(var(--s-2) + env(safe-area-inset-bottom, 0px));
  background: var(--ink);
  border-top: 1px solid var(--line-dark);
  box-shadow: 0 -6px 22px rgba(11,36,54,.24);
}
.callbar__inner { display: flex; gap: var(--s-2); }
.callbar .btn { flex: 1 1 auto; min-height: 52px; font-size: var(--t-0); }
.callbar .btn--quiet2 {
  flex: 0 1 40%;
  background: transparent;
  color: var(--white);
  border-color: rgba(255,255,255,.42);
}
.callbar .btn--quiet2:hover { background: rgba(255,255,255,.14); }
.callbar__note {
  display: block;
  margin-bottom: var(--s-1);
  text-align: center;
  /* Readable size beats a tracked micro-label here: this is the line that makes
     someone tap Call at 1am, on the primary device, for an audience that skews
     older and stressed. Uppercase + 0.14em tracking pushed it onto two lines and
     cost 27px of fold, so it drops to sentence case — which reads warmer too. */
  font-size: var(--t--1);
  font-weight: 600;
  letter-spacing: 0;
  color: var(--on-ink-quiet);
}

/* Short viewports (landscape phones, 320x568) cannot spare 25% of the screen
   for a fixed bar. The buttons stay; the reassurance line goes. */
@media (max-height: 460px), (max-width: 360px) {
  .callbar__note { display: none; }
  :root { --callbar-h: 68px; }
}

@media (max-width: 767px) {
  :root { --callbar-h: 84px; }
  .callbar { display: block; }

  /* Nothing re-shows the contact group here any more. It used to need this
     because an older rule hid it on phones; now the only rule that hides it is
     the 480 one, and this block comes AFTER that -- so left in place at equal
     specificity it would have undone the hide, which is the same order trap
     the header gap fell into last chunk. */
  .shf-header__bar { gap: var(--s-3); }

  /* The header does NOT stick on phones. Its call button is hidden here (the
     fixed bar carries the call instead), so all it would pin to the top is a
     logo and a menu toggle: 64px of a 667px screen, permanently, on top of the
     95px bar at the bottom — 23.8% of the viewport given over to chrome.
     Someone deep in a 7,500px page wants the phone, which is already fixed,
     far more than the nav. Desktop keeps it sticky: there the header carries
     the number itself. */
  .shf-header { position: static; }
}

/* 8px between the three at 360, not 12, and this has to come after the block
   above: both are (0,0,1,0) and on the first attempt the wider query won on
   order. With the sublabel out of the layout the wordmark came to within 4px
   of fitting -- 129px of box against 133px of text -- and these two gaps are
   the only slack left that costs nothing, the toggle being at its 44px floor
   and the number already the smaller of the two lines. */
@media (max-width: 480px) {
  .shf-header__bar { gap: var(--s-2); }
}

/* ==========================================================================
   9. HERO
   ========================================================================== */

.hero {
  position: relative;
  background: var(--ink);
  color: var(--on-ink);
  padding-block: clamp(var(--s-6), 5vw, var(--s-9)) 0;
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(900px 460px at 88% -20%, rgba(0,191,166,.20), transparent 64%);
  pointer-events: none;
}
.hero__grid {
  position: relative;
  display: grid;
  gap: var(--s-7);
  padding-bottom: var(--s-7);
}
/* This column's spacing is set by its own content, not by the height of the
   column beside it.

   It used to be justify-content: space-between, which spreads ALL free space
   into the gaps between the children. That was fine while the right column
   was just the offer card: both columns came to 704px, there was no free
   space, and the gaps were the margins below. Adding the illustration under
   the card took the right column to 1013px. The left column stretched to
   match, and space-between handed all 309px of the difference to the five
   gaps -- 62px into each, so the eyebrow-to-h1 gap went 12px -> 74px and the
   sub-to-lead gap 64px -> 126px. Nothing about the copy changed; the artwork
   re-spaced it from the other side of the hero.

   flex-start leaves the margins in charge, so the words end level with the
   bottom of the card and the illustration hangs below them. The surplus is
   now empty navy under the buttons instead of air inside the sentences.

   The flex context itself STAYS. These children set margin-bottom AND
   margin-top on adjacent siblings (--s-6 under .hero__sub, --s-6 over
   .hero__lead); flex does not collapse those, block flow does. Dropping to
   display: block to "simplify" would silently halve that 64px gap. */
@media (min-width: 1024px) {
  /* body.home only. The situation pages use .hero__words too, for a
     breadcrumb, an H1 and a paragraph, and spreading those down a column
     is not balance, it is a gap. Only the homepage has a 771px card
     beside the words.

     Was .hero .wrap, which matched two elements once the illustration got
     its own .wrap: the grid this was written for, and the art rail below
     it. The art rail is display: block, so align-items was inert there --
     luck, not design, and one display change away from not being. Scoped
     to .hero__grid, the one it actually means.

     Was also align-items: stretch, which is the reason .hero__aside had
     342px of unclaimed space inside its own box: a grid row's height comes
     from its tallest item regardless of align-items -- words, at 652px,
     was always going to set this row's height, stretch or not -- but
     stretch then forced EVERY item in the row to that height too, so the
     310px card sat inside a 652px aside box with nothing in the remaining
     342px. start leaves each item at its own natural height, top-aligned:
     the card still sits level with the words column, at 310px, and the
     652px the row needs for the words stays exactly what it was. Nothing
     was added to the aside and nothing was padded -- the box is just
     honest about what is in it now. */
  body.home .hero__grid { align-items: start; }
  body.home .hero__words {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
  }
}
.hero__words { max-width: none; }

/* Column two: the card, then the illustration -- both are DOM children of
   this element at every width. No gap set here at the base rule: below
   1024 the two must sit exactly as flush as the card and the grid's own
   padding-bottom used to leave them (the art was a sibling of the grid
   then, not a flex child of this column, so no gap ever touched it). A
   gap here unconditionally would add 24px below 1024 that rail never
   had. */
.hero__aside { display: flex; flex-direction: column; }
@media (min-width: 1024px) {
  /* 24px between the card and the illustration, now that they share the
     416px column and the art is short enough not to set the row. */
  .hero__aside { gap: var(--s-5); }
}

/* The illustration, below the card in the same column at every width.

   280px, not --narrow (680px, the SVG's intrinsic width). The hero's job is
   the headline and the form; the house is a supporting illustration under
   both, not a second hero. At 680px on a 4:3 drawing the band ran 510px
   tall -- taller than the card beside it -- and read as the page's main
   image rather than a footnote to it. 280px is well below that: a compact
   4:3 illustration (280x210) that sits under the card without competing
   with it for the first thing a reader's eye goes to. Shorter than the
   310px card, too, which is why it can live inside the aside without
   becoming the tallest thing in the row: 310 + 24 + 210 = 544, against the
   words column's own 553 -- the words still set the row's height, not this.

   No panel, no radius, no shadow. The roof, eaves, chimney cap and the lower
   ground strip are --surface #F5F9F8 in the SVG itself, 14.99:1 against the
   ink ground, so the drawing carries its own contrast and needs nothing
   behind it.

   overflow stays. It clips nothing, but it is what stops a future viewBox
   change bleeding the drawing out of its box. margin-inline: auto centres it
   inside whatever .hero__aside gives it -- the full row below 1024, the
   416px column from 1024 up -- so nothing here needs to know which. */
.hero__art {
  max-width: 280px;
  margin-inline: auto;
  overflow: hidden;
}

/* Flush with the band edge, below 1024 only.
   .hero__grid always carries its own padding-bottom -- 48px at the base
   rule, 32px under the max-width: 767px override further down -- and
   below 1024 the illustration is the grid's last thing, so that padding
   became 32-48px of bare navy under the house's own road strip before the
   trust strip started. The art's own pavement line (the SVG's last shape,
   full width of the drawing) is meant to read AS the boundary between the
   two bands, not sit above one. Scoped to .hero specifically -- the
   situation pages' hero also carries .hero__grid (as .page-hero__grid)
   and never holds the illustration, so their own padding-bottom must not
   change. Above 1024 the aside is a narrower column beside the words and
   the grid's padding-bottom belongs to the row as a whole, not to this
   column alone -- left untouched. */
@media (max-width: 1023px) {
  .hero .hero__grid { padding-bottom: 0; }
}

/* The calendar's own rail, directly above the illustration's and built the
   same way: a .wrap for the 1200px content rail, margin-inline: auto to
   centre inside it. Not capped at --narrow -- the illustration is a picture
   that gets worse enlarged past its own pixels, and this is a list of text
   that does not. --white/--r-lg/--shadow-3 replace the white ground .plan
   was providing: .steps__what and the "today" dot are --ink, which on the
   bare hero -- also #0B2436 -- would be invisible without one. */
.hero__art svg { display: block; width: 100%; height: auto; }


/* Design h1 is clamp(33px,5.4cqw,60px). 33px is --t-3 almost exactly; 60px
   sits between --t-5 and --t-6, and --t-6 is what the page already ships and
   what the situation pages use, so the scale stays at eight steps. */
.hero h1 { color: var(--white); margin-bottom: var(--s-4); font-size: var(--t-3); }
@media (min-width: 1024px) { .hero h1 { font-size: var(--t-6); } }

.hero h1 .accent, .hero h1 em { font-style: normal; color: var(--primary); }
.hero__sub {
  font-size: var(--t-1);
  line-height: 1.42;
  letter-spacing: var(--track-tight);
  color: var(--on-ink);
  max-width: 42ch;
  margin-bottom: var(--s-6);
}

/* The client's second heading and line. A clear step below the h1 so the hero
   reads as one headline with a supporting statement, not two competing
   titles.

   NO MARGIN-TOP. .hero__sub already carries --s-6 below it, and at >= 1024
   .hero__words is a flex column, where margins do not collapse -- so a
   --s-6 here stacked on that one and opened a 64px hole in the middle of
   the column, the largest gap in the hero and twice any other. The step
   below the h1 is carried by the 32px that paragraph already owns. */
.hero__lead {
  margin: 0 0 var(--s-2);
  font-size: var(--t-1);
  line-height: 1.2;
  letter-spacing: var(--track-h3);
  color: var(--white);
}
.hero__leadsub {
  margin: 0 0 var(--s-5);
  max-width: 46ch;
  font-size: var(--t-0);
  line-height: 1.6;
  color: var(--on-ink);
}
@media (min-width: 1024px) { .hero__lead { font-size: var(--t-2); } }


/* ==========================================================================
   THE OFFER BAND
   Step one asks for a postcode and nothing else. This band exists to make
   that look as cheap as it actually is, so the card gets the width the hero
   column used to deny it. White, because the trust strip below is --surface
   and two tinted bands must never touch.
   ========================================================================== */
/* No max-width here: every other section wrap is --wide, and capping this one
   started the band 152px further right than the rest of the page. The card
   keeps its measure by capping itself instead, flush to the same left edge. */
/* Capping the wrap would have made this the only section not starting at the
   page left edge, which is the defect chunk 4.5 removed. Centring the card
   inside a full-width wrap fixes the same complaint — a card stranded to the
   left of 300px of empty surface — without breaking the alignment: 152px of
   band each side reads as deliberate, where 0 and 304 did not. */
/* Arrows sit at the end of the label and never shrink. Scoped to the
   postcode form, which is the only place an arrow renders: homepage,
   contact and the situation pages. Chunk 2 left this on every button on
   every template; .offerband alone would have been too tight and dropped
   the rule on the other two. */
.pc-form .btn svg { flex: 0 0 auto; }

/* FIX 3 — the eyebrow sits in a pill, not as loose teal text. Scoped to the
   hero so the label elsewhere is untouched. */
.hero .label {
  display: inline-block;
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--primary-28);
  border-radius: var(--r-pill);
  background: var(--primary-12);
}

/* The call block — primary path, deliberately outweighs the form. */
.callblock { max-width: 26rem; }
.callblock__label {
  display: block;
  margin-bottom: var(--s-2);
  font-size: var(--t--1);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--primary);
}
.callblock__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  width: 100%;
  min-height: 68px;
  padding: var(--s-3) var(--s-5);
  background: var(--cta);
  border-radius: var(--r-md);
  color: var(--white);
  text-decoration: none;
  transition: background var(--ease);
}
.callblock__btn:hover, .callblock__btn:focus-visible { background: var(--cta-hover); color: var(--white); }
.callblock__num {
  font-size: clamp(var(--t-2), 6vw, var(--t-3));
  font-weight: 700;
  line-height: 1;
  letter-spacing: var(--track-h3);
  white-space: nowrap;
}
.callblock__note {
  margin-top: var(--s-3);
  font-size: var(--t--1);
  line-height: 1.5;
  color: var(--on-ink);
}
/* FIX 6 — in the hero this is the line that answers "what happens if I ring".
   At --t--1 it read as fine print. Body size and real spacing, hero only.
   --s-2 and not --s-5: the strap answers the buttons, so it belongs to them.
   The flex gap on .callblock--pair adds --s-3 on top of whatever is set
   here, so 8 renders as 20px of air under the buttons, not 8. */
.hero .callblock__note { margin-top: var(--s-2); font-size: var(--t-0); line-height: 1.6; max-width: 44ch; }
/* Direct child only — must not reach the <strong> inside the placeholder flag. */
.callblock__note > strong { color: var(--white); display: block; }

/* Modifiers, after the base rules they override. The paired hero version was
   previously declared ABOVE the 26rem cap and lost to it, which rendered both
   buttons stacked at 416px inside a 656px column. Base first, modifier second,
   so the cascade reads in the order the file does. */
/* The label and the note run full width; the two buttons share the row
   between them and stack when there is not room for both. */
.callblock--pair {
  max-width: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}
/* Both run full width. No margin-top reset here: it has the same specificity
   as .hero .callblock__note, and once these modifiers moved below the base
   rules it started winning and silently took 24px off the reassurance line.
   The flex gap already handles the spacing. */
.callblock--pair .callblock__label,
.callblock--pair .callblock__note { flex: 1 0 100%; }
.callblock--pair .callblock__btn { flex: 1 1 15rem; width: auto; }
.callblock__alt {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  flex: 1 1 15rem;
  min-height: 68px;
  padding: var(--s-3) var(--s-5);
  border: 2px solid rgba(255, 255, 255, .34);
  border-radius: var(--r-md);
  color: var(--white);
  font-size: var(--t-0);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  text-decoration: none;
}
.callblock__alt:hover, .callblock__alt:focus-visible {
  border-color: var(--primary);
  background: var(--primary-12);
  color: var(--white);
}

/* Two columns again: words left, card right, as the design has it. */
@media (min-width: 1024px) {
  .hero__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
    gap: var(--s-8);
    align-items: center;
    padding-bottom: var(--s-8);
  }
}

/* Single-column widths only: stop the display type running the full container.
   At >=1024px the grid column already constrains it. */
@media (min-width: 768px) and (max-width: 1023px) {
  .hero h1, .hero__sub { max-width: 20ch; }
}

/* --- Step two: get out of the way -----------------------------------------
   Once a postcode is in, the hero copy has done its job. Keeping it above the
   card on a phone pushed the submit button ~350px below the fold. The fixed
   call bar still carries the phone path, so nothing is lost. */
@media (max-width: 1023px) {
  .shf-step-2 .hero__words { display: none; }
  /* Step two gets out of the way; a decorative panel is the first thing
     that should go with the copy it accompanied. */
  .shf-step-2 .hero__art { display: none; }
  .shf-step-2 .hero { padding-block: var(--s-4) 0; }
  .shf-step-2 .hero__grid { gap: 0; padding-bottom: var(--s-6); }
}
@media (min-width: 1024px) {
  .shf-step-2 .hero .label,
  .shf-step-2 .hero__sub { display: none; }
  .shf-step-2 .hero h1 { font-size: var(--t-3); }
}

/* --- The mobile fold ------------------------------------------------------
   Measured at 375x812 before this block existed: the hero call button landed
   at 718px with the fixed call bar starting at 721px, so 65 of its 68 pixels
   were hidden underneath it, and the postcode field sat 215px below the fold.
   On the primary device there was not one reachable door above the fold.

   This block must stay AFTER the rules above — same specificity, so position
   in the file is what decides it. */
@media (max-width: 767px) {
  .hero { padding-block: var(--s-5) 0; }
  .hero__grid { gap: var(--s-5); padding-bottom: var(--s-7); }
  .hero h1 { margin-bottom: var(--s-3); }
  .hero__sub {
    font-size: var(--t-0);
    line-height: 1.5;
    margin-bottom: var(--s-4);
    max-width: none;
  }
  /* Tighter tracking keeps the eyebrow on one line at 375px. */
  .hero .label { margin-bottom: var(--s-2); letter-spacing: 0.06em; }
  .callblock__note { margin-top: var(--s-2); margin-bottom: var(--s-2); font-size: var(--t--1); }
  /* The second reassurance sentence is worth 45px of fold. It is repeated in
     the FAQ and the call band, so on a phone it goes. */
  .callblock__reassure { display: none; }
  /* FIX B — the GREEN paragraph repeats what the h3 above it and hero__sub
     already say, and it is 114px of a hero that now also holds the card and
     its dated list. Hidden here only: it stays in the DOM and renders from
     1024px up. */
  .hero__leadsub { display: none; }
  .callblock__note--flag { margin-bottom: var(--s-3); }
  /* The card is inside the hero again, so on a phone the hero carries the
     headline block, two buttons AND a 283px card. Every gap in it is pulled
     in as far as it can go without the block reading as cramped. What is
     left is the sum of its parts, not slack. */
  .hero__grid { gap: var(--s-4); padding-bottom: var(--s-6); }
  .hero__lead { margin-top: var(--s-4); }
  .hero__leadsub { margin-bottom: var(--s-4); }
  .hero .callblock__note { margin-top: var(--s-2); }
  .callblock--pair { gap: var(--s-2); }
  .plan__top { padding: var(--s-4) var(--s-4) var(--s-3); }
  .plan__title { font-size: var(--t-0); }
  .plan__sub { margin-bottom: var(--s-3); }
}

/* ==========================================================================
   10. CERTAINTY CALENDAR — the signature element
   Real, working-day-aware dates computed server side.
   ========================================================================== */

.plan {
  background: var(--white);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  color: var(--body);
  overflow: hidden;
}

.plan__top { padding: var(--s-5) var(--s-5) var(--s-4); border-bottom: 1px solid var(--line); }
@media (min-width: 560px) { .plan__top { padding: var(--s-6) var(--s-6) var(--s-5); } }

.plan__title { font-size: var(--t-1); margin-bottom: var(--s-1); letter-spacing: var(--track-h3); }
.plan__sub { font-size: var(--t--1); margin-bottom: var(--s-4); color: var(--body); }

.plan__body { padding: var(--s-5); }
@media (min-width: 560px) { .plan__body { padding: var(--s-6); } }

/* The dated compartments as a disclosure, homepage card only. Closed below
   the 1024 rail, where the card is a 589px block sitting under the whole hero
   and pushing the submit button off the first screen. Forced open above it.
   Content is in the DOM in both states, so nothing is hidden from a crawler.
   Two force-open mechanisms because browsers moved from one to the other:
   ::details-content is the current spec, and display on the non-summary
   children covers engines that have not adopted it. */
.plan__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  min-height: 44px;
  padding: var(--s-3) var(--s-5);
  border-top: 1px solid var(--line);
  font-size: var(--t--1);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  color: var(--hover);
  cursor: pointer;
  list-style: none;
}
.plan__summary::-webkit-details-marker { display: none; }
.plan__summary::after {
  content: "";
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--hover);
  border-bottom: 2px solid var(--hover);
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--ease);
}
.plan__more[open] > .plan__summary::after { transform: translateY(2px) rotate(-135deg); }
.plan__more[open] > .plan__summary { border-bottom: 1px solid var(--line); }
.plan__summary:hover { color: var(--ink); }
@media (min-width: 1024px) {
  .plan__more > .plan__summary { display: none; }
  .plan__more > *:not(summary) { display: block; content-visibility: visible; }
  .plan__more::details-content { content-visibility: visible; block-size: auto; }
}

.plan__caption {
  padding: var(--s-4) var(--s-5);
  background: var(--surface);
  border-top: 1px solid var(--line);
  font-size: var(--t--1);
  line-height: 1.5;
}
@media (min-width: 560px) { .plan__caption { padding: var(--s-4) var(--s-6); } }

/* --- the card in context ------------------------------------------------
   These four sat in three separate places: two up in the offer-band area and
   one a thousand lines down beside the page hero, with the component itself
   between them. All are (0,0,2,0) against the component's (0,0,1,0), so they
   won on specificity wherever they were written and moving them changes
   nothing -- which the before/after computed digests confirm. What it buys is
   that .plan is now described in one place. */
/* The card lives in the hero again, so the teal top rule moves with it. */
.hero .plan { border: 1px solid var(--line); border-top: 4px solid var(--primary); }

/* Design: clamp(25px,3.1cqw,38px), which is the global h2 ramp already
   (--t-2 -> --t-4), so the card stops overriding it down to --t-1. Scoped to
   .offerband so the contact page and the situation pages do not move. */
/* In a 26rem column the title is a card heading, not a section heading, so it
   drops from the --t-2/--t-4 ramp it used at full band width. */
.hero .plan__title { font-size: var(--t-1); letter-spacing: var(--track-h3); }
@media (min-width: 1024px) { .hero .plan__title { font-size: var(--t-2); } }

.plan__lead {
  margin: 0 0 var(--s-4);
  font-size: var(--t-0);
  line-height: 1.55;
  color: var(--body);
}
.page-hero__grid .plan { position: relative; z-index: 1; }

/* The dated steps */
.steps { list-style: none; margin: 0; padding: 0; }
.steps li {
  position: relative;
  display: grid;
  grid-template-columns: var(--s-6) 1fr;
  gap: var(--s-4);
  padding-bottom: var(--s-5);
}
.steps li:last-child { padding-bottom: 0; }

/* Connector line between markers */
.steps li::before {
  content: "";
  position: absolute;
  left: calc(var(--s-4) - 1px);
  top: var(--s-5);
  bottom: calc(var(--s-1) * -1);
  width: 2px;
  background: var(--primary-28);
}
.steps li:last-child::before { display: none; }

.steps__dot {
  position: relative;
  z-index: 1;
  width: var(--s-6);
  height: var(--s-6);
  border-radius: 50%;
  background: var(--primary);
  display: grid;
  place-items: center;
  color: var(--ink);
}
.steps li.is-now .steps__dot { background: var(--ink); color: var(--primary); }
/* Upcoming steps: an empty node on the line. No tick — nothing is done yet. */
.steps li:not(.is-now) .steps__dot {
  background: transparent;
  box-shadow: inset 0 0 0 3px var(--hover);
}

.steps__date {
  display: block;
  font-size: var(--t--1);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--hover);
  margin-bottom: 2px;
}
.steps li.is-now .steps__date { color: var(--ink); }
/* The offer step carries the page's headline promise, so it gets the only
   accent in the list: a teal duration where the others show a grey date,
   and a tinted ground so the eye lands on it before the dates. */
.steps li.is-lead { background: var(--primary-12); border-radius: var(--r-sm); }
.steps__date--lead { color: var(--hover); font-weight: 700; letter-spacing: var(--track-label); text-transform: uppercase; }
.steps li.is-lead .steps__dot { color: var(--hover); }
.steps li.is-lead .steps__what { color: var(--ink); }

.steps__what {
  display: block;
  font-size: var(--t-0);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  color: var(--ink);
  line-height: 1.3;
}

/* Postcode row */
.pc-form { display: flex; flex-wrap: wrap; gap: var(--s-2); }
/* FIX 2 — in the hero card the button sits under the input at full width, as
   the design has it. .hero scopes this to the homepage: the same card on the
   contact page and the situation pages keeps its side-by-side row. */
.hero .plan .pc-form { display: block; }
.hero .plan .pc-form__field { margin-bottom: var(--s-3); }
.hero .plan .pc-form .btn { width: 100%; }
.pc-form__field { flex: 1 1 10rem; }
.pc-form input[type="text"] {
  width: 100%;
  min-height: 52px;
  padding: var(--s-3) var(--s-4);
  background: var(--white);
  border: 2px solid var(--field-line);
  border-radius: var(--r-md);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-1);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.pc-form input[type="text"]::placeholder { font-weight: 400; letter-spacing: 0; text-transform: none; color: #8D9AA0; }
.pc-form input[type="text"]:focus { border-color: var(--ink); }
.pc-form .btn { flex: 0 0 auto; min-height: 52px; }

/* Coverage answer returned after step one */
.coverage {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  margin-bottom: var(--s-3);
  padding: var(--s-2) var(--s-3);
  background: var(--primary-12);
  border-left: 3px solid var(--primary);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: var(--t--1);
  line-height: 1.45;
  color: var(--ink);
}
.coverage strong { display: block; font-size: var(--t-0); }
.coverage svg { flex: 0 0 auto; color: var(--hover); margin-top: 2px; }
.coverage--outside { background: var(--warn-bg); border-left-color: var(--warn); }
.coverage--outside svg { color: var(--warn); }

/* ==========================================================================
   SELL IN 3 EASY STEPS  (#how-it-works)
   Homepage only. The dated Certainty Calendar still lives in shf_flow() and
   still renders on /sell-problem-property/; this carries no dates at all.
   Icon above the label, not beside it: the three items read as a row of
   equals rather than a list with markers down the left, which is what the
   numbered version looked like.
   ========================================================================== */
.steps3 {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-4);
}
/* Cards on the brand ground, the same treatment the won't-do band uses, so
   the two bands rhyme rather than adding a third card idiom to a page that
   already has too many. The disc needed an edge to sit on; on bare teal it
   had nothing behind it. */
.steps3__item {
  position: relative;
  padding: var(--s-5);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}
/* A stretched link, not an <a> wrapped round the card. The whole card is
   the target, but the accessible name stays the step label instead of the
   label plus its sentence read out as one string. */
.steps3__link { color: inherit; text-decoration: none; }
.steps3__link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--r-md);
}
/* The ring belongs on the card, not on three words inside it. Where :has()
   is unsupported the link keeps the global :focus-visible ring, so a focus
   state is never absent — only smaller. */
.steps3__item:has(.steps3__link:focus-visible) {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
}
/* Without :has() the card ring cannot be drawn and the lift never matches
   either, so a keyboard user would get the default inline ring round three
   words and nothing else. This makes that fallback deliberate: a ring with
   real offset, on the link itself. */
@supports not selector(:has(*)) {
  .steps3__link:focus-visible {
    outline: 3px solid var(--ink);
    outline-offset: 4px;
    border-radius: var(--r-sm);
  }
}
/* A tinted disc, not a white one. Three white circles on the brand ground
   would introduce a fourth surface colour to a band that only needs two. */
.steps3__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.25rem;
  height: 3.25rem;
  margin-bottom: var(--s-4);
  border-radius: var(--r-pill);
  background: rgba(11, 36, 54, .1);
  color: var(--ink);
}
.steps3__what {
  margin: 0;
  font-size: var(--t-2);
  line-height: 1.15;
  letter-spacing: var(--track-h3);
  color: var(--ink);
}
/* Above the stretched ::after, so the sentence stays selectable. The label
   is still the link and the rest of the card is still the target. */
.steps3__why {
  position: relative;
  z-index: 1;
  margin: var(--s-2) 0 0;
  font-size: var(--t-0);
  line-height: 1.55;
  color: var(--ink);
  max-width: 32ch;
}
@media (min-width: 1024px) {
  .steps3 { grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }
}

/* Below the rail the disc sits beside the label rather than above it, and
   the card tightens to --s-4. Icon-above-label is the old site's desktop
   arrangement; carried down to a stacked column it cost 87px of height for
   three cards and pushed everything below them further down the phone. */
@media (max-width: 1023.98px) {
  .steps3__item {
    padding: var(--s-4);
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: var(--s-4);
    align-items: center;
  }
  .steps3__icon { width: 2.5rem; height: 2.5rem; margin-bottom: 0; }
  .steps3__why { grid-column: 1 / -1; margin-top: var(--s-3); max-width: none; }
}

/* ==========================================================================
   BENEFITS
   The trust strip asserts in three words and a solid glyph. This explains
   in a sentence under a stroked one. Cards, not a ribbon: more air, larger
   type, left aligned, on --surface between two white bands.
   ========================================================================== */
/* Ground comes from .band--surface in the markup. The cards here always
   carried the same white, the same 1px --line border, the same 10px radius
   and the same 24px padding as the won't-do and three-step cards. What made
   them read as a lighter, different component was the band under them:
   white cards on a white band leave the border nothing to sit against.
   --surface keeps the run free of adjacent repeats (teal above, ink below). */
.benefits__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-4);
  grid-template-columns: 1fr;
}
.benefits__item {
  padding: var(--s-5);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}
.benefits__icon { display: block; margin-bottom: var(--s-3); color: var(--hover); }
.benefits__label {
  margin: 0 0 var(--s-2);
  font-size: var(--t-1);
  line-height: 1.2;
  letter-spacing: var(--track-h3);
  color: var(--ink);
}
.benefits__body {
  margin: 0;
  font-size: var(--t-0);
  line-height: 1.55;
  color: var(--ink);
}
/* The 24-hour promise is the headline claim on this page, so its card carries
   the only accent in the grid. */
.benefits__item--lead { border-top: 4px solid var(--primary); }
@media (min-width: 640px) {
  .benefits__list { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  /* Five items over SIX columns, not three. Three columns cannot divide a
     row of two evenly, so the second row was a 752px card beside a 368px
     one. Six columns divide by both: three cards at two columns each on
     row one, two at three columns each on row two. Nothing spans a gap it
     has not earned, and it echoes the won't-do band's three-then-one. */
  .benefits__list { grid-template-columns: repeat(6, 1fr); }
  .benefits__item { grid-column: span 2; }
  .benefits__item:nth-child(4),
  .benefits__item:nth-child(5) { grid-column: span 3; }
}


/* ==========================================================================
   THE AGENT CASE
   Client copy, set as plain prose above the two-column comparison. It is
   deliberately NOT styled as a callout: the comparison below is the honest
   part of this section and must stay the loudest thing in it.
   ========================================================================== */
/* Base rules first, then the modifiers. They were the other way round: the
   1024 block sat between .agentcase and .agentcase__title, so the media query
   preceded two of the three rules it modifies. Nothing was overriding wrongly
   -- the properties happened not to collide -- but the next edit to it would
   have had a coin flip for a cascade. */
.agentcase { max-width: 62ch; }
.agentcase__title {
  margin: 0 0 var(--s-4);
  font-size: var(--t-1);
  letter-spacing: var(--track-h3);
  color: var(--ink);
}
/* The heading's BOX already spanned both columns -- measured at 1136 of 1136 at
   1280, so column-span was applying. What made it read as a header for the left
   column only is that its ink stops at 501px, which is where column one stops,
   and nothing said otherwise. A rule the full width of the block says it: the
   same 2px currentColor at .16 that .band-head--ruled uses, so this is the
   system's existing device rather than a second one. */
.agentcase__title::after {
  content: "";
  display: block;
  height: 2px;
  margin: var(--s-4) 0 0;
  background: currentColor;
  opacity: .16;
}
.agentcase p { margin: 0 0 var(--s-4); font-size: var(--t-0); line-height: 1.6; }
/* Below 1024 the illustration follows the prose at the 62ch measure. */
.agentcase__art { display: block; width: 100%; height: auto; margin-top: var(--s-5); }

/* Flush with the band edge, below 1024 only -- the same fix the semi's
   illustration already has. .band--compact always carries its own
   padding-block (48px below 1024), and below 1024 the illustration is
   the last thing in this band, so that bottom padding sat as bare
   --surface under the terraced houses' own road strip before the next
   band (Honest Comparison, --ink) began. The road strip is the SVG's
   last shape, full width of the drawing, and reads as the boundary
   between the two bands rather than as a gap above one.

   Scoped with :has() to the one band that holds this illustration --
   .band--compact styles several unrelated bands ("Sell in 3 easy
   steps", the FAQ) and their own bottom padding must not change. At
   >= 1024 the illustration sits BESIDE the prose in a two-column grid,
   centred on the row by align-items: center because it is shorter than
   the prose (364px against 371px) -- flush would misalign it against
   the text it sits next to, so this stays scoped to below the rail. */
@media (max-width: 1023px) {
  .band:has(.agentcase--illustrated) { padding-bottom: 0; }
}

/* The fifth paragraph is the answer to the question the heading asks; the four
   above it are the complaint. It read exactly like them -- same 17px, same 400,
   same --body grey -- so the block was five equal statements and the turn in
   the argument was invisible.

   Three signals, not one, because the teal rule alone is 2.20:1 against this
   band and that is decoration, not a marker: the rule, one step up in size, and
   --ink instead of --body, which also takes the paragraph from 5.18:1 to
   14.99:1. The device is .sit-close's, already used twice on this page for the
   same job -- the last word of a section behind a teal edge.

   It applies at every width, so the hierarchy survives the phone, where the
   block is one column of five paragraphs and needs it most. */
.agentcase p:last-child {
  /* Its own gap above, so the turn is separated at every width and not only
     where the 1024 rule gives it 32. Adjacent margins collapse, so this is
     24 rather than 24 on top of the 16 the paragraph above already has. */
  margin: var(--s-5) 0 0;
  padding-left: var(--s-5);
  border-left: 3px solid var(--primary);
  font-size: var(--t-1);
  line-height: 1.4;
  letter-spacing: var(--track-h3);
  color: var(--ink);
}

/* Below 768 the edge moves from the left of the paragraph to the top of it.

   A left edge is only visible once the eye is already on the line. A rule
   across the top is visible from further up the scroll, and at 390 the reader
   meets 508px of grey complaint before this sentence -- which is the only one
   that says what the business does. The copy cannot be reordered, so the
   marker has to arrive earlier instead. Same 3px, same --primary, same three
   signals; only the side changes. */
@media (max-width: 767px) {
  .agentcase p:last-child {
    padding-left: 0;
    padding-top: var(--s-5);
    border-left: 0;
    border-top: 3px solid var(--primary);
  }
}

/* Two columns above 1024, and the answer spans both beneath them.

   That is what balances the columns, and it is not a trick: five items of
   heights 54, 82, 54, 54, 82 cannot balance across two columns whichever way
   they split -- 3/2 gives 222 against 152 and 2/3 gives the same gap mirrored,
   because break-inside: avoid means the browser can only break between whole
   paragraphs. Taking the fifth out of the flow leaves four, which do balance,
   and it puts the conclusion where a conclusion goes. One change, two problems.

   Below 1024 it stays one column at the 62ch measure. Two columns at tablet
   would be about 43ch each, which is a cramped measure for prose, and the
   brief rules it out anyway. */
@media (min-width: 1024px) {
  .agentcase { max-width: none; columns: 2; column-gap: var(--s-8); }
  .agentcase__title { column-span: all; }
  .agentcase p { break-inside: avoid; }

  /* THE ILLUSTRATED VARIANT REPLACES THE TWO-COLUMN TREATMENT, it does not
     sit beside it. columns:2 exists because the prose at its 62ch measure
     left ~404px of empty band after it -- the note above says so. The
     illustration now occupies exactly that space, which answers the same
     problem with the same geometry; running both would solve it twice and
     would cut the measure to ~26ch a column. So the prose returns to one
     column at its natural measure and the art takes the width the second
     text column had.

     Two auto-placed children in row two -- prose left, art right -- rather
     than a row span: -1 resolves against the EXPLICIT grid, and with the
     paragraphs auto-placing there is only one explicit row, so a 2/-1 span
     put the art above the heading. The wrapper makes the prose one grid
     item and the placement needs no spans at all. */
  .agentcase--illustrated {
    columns: auto;
    display: grid;
    grid-template-columns: minmax(0, 62ch) minmax(0, 1fr);
    column-gap: var(--s-8);
    /* end, not center. The prose sets the row -- it is the taller of the
       two, at 370.8px against the art's 364.4px -- so centring left 3.2px
       of slack above the art AND 3.2px below it, and that lower sliver put
       the art's own bottom edge 3.2px off the prose's last line, floating
       it rather than grounding it. end puts all 6.4px above the art
       instead, so its bottom edge lands exactly on the prose's, and the
       one place this rule is used is this one class -- no :has() needed,
       unlike .band--compact's shared padding-block. */
    align-items: end;
  }
  .agentcase--illustrated .agentcase__title { grid-column: 1 / -1; }
  /* The measure cap lives on the block below 1024; up here the grid column
     carries it, so the prose wrapper just fills its column. */
  .agentcase--illustrated .agentcase__prose { grid-column: 1; }
  .agentcase--illustrated .agentcase__art { grid-column: 2; margin-top: 0; }

  /* Indented to column two's left edge: 50% of the block plus half the gap.

     Spanning from the block's left edge left the sentence stopping at 732 of
     1136 with 404px of nothing after it -- the ragged bottom the column fix
     removed, moved down one element. Centring it was the other candidate and
     is worse: it puts the left edge at 202, which aligns with nothing, and
     leaves a void on both sides instead of one.

     Indented, both edges land on real geometry -- left on column two, right on
     the block -- the space to its left reads as column one's footprint rather
     than a trailing void, and the eye leaves column two and drops straight
     down instead of travelling back across the page.

     The measure cap stays declared and simply stops binding: the available
     width is 536, under the 732 the cap allows, so the sentence sets to three
     lines at about 50 characters rather than four at 38. */
  .agentcase p:last-child {
    column-span: all;
    margin-top: var(--s-6);
    margin-left: calc(50% + var(--s-8) / 2);
  }
  /* No column two to indent to once the art has it. */
  .agentcase--illustrated p:last-child { margin-left: 0; margin-top: var(--s-4); }
}

/* ==========================================================================
   12. HONEST COMPARISON
   ========================================================================== */

/* Stretched, not content-sized. An earlier note recorded that stretching
   these cards "manufactured the emptiness" — it did, because the shorter card
   grew a void at its foot. It works now only because the shorter column ends
   in the protected "If you can wait" line, pinned down with margin-top:auto,
   so the extra height becomes separation and the line anchors the card. */
#is-this-right .compare { display: grid; gap: var(--s-4); align-items: stretch; }
@media (min-width: 800px) { #is-this-right .compare { grid-template-columns: 1fr 1fr; gap: var(--s-5); } }

/* Padding and radius match the card system the other three bands share.
   What is deliberately NOT shared is the pair's own contrast: the teal
   border and the single shadow on our column are the only thing saying
   these two are being compared rather than listed. */
#is-this-right .compare__col {
  display: flex;
  flex-direction: column;
  padding: var(--s-5);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--white);
}
#is-this-right .compare__col > p:last-child { margin-top: auto; }
#is-this-right .compare__col--ours { border-color: var(--primary); box-shadow: var(--shadow-1); }
#is-this-right .compare__col { color: var(--ink); }
#is-this-right .compare__col h3 { margin-bottom: var(--s-5); color: var(--ink); }
#is-this-right .compare__list { list-style: none; margin: 0; padding: 0; }
#is-this-right .compare__list li {
  display: grid;
  grid-template-columns: var(--s-5) 1fr;
  gap: var(--s-3);
  padding-block: var(--s-3);
  border-top: 1px solid var(--line);
  font-size: var(--t-0);
}
#is-this-right .compare__list li:first-child { border-top: 0; padding-top: 0; }
#is-this-right .compare__list svg { margin-top: 3px; color: var(--hover); }
#is-this-right .compare__list .is-cost svg { color: var(--body); }
#is-this-right .compare__list .is-cost { color: var(--body); }

/* ==========================================================================
   13. SITUATIONS + AREAS
   ========================================================================== */

/* Two readable columns of prose. Keeps a long passage off a single thin
   measure without turning it back into a list. */
.sit-intro { max-width: 62ch; font-size: var(--t-0); margin: 0 0 var(--s-4);
             color: var(--body); }
/* The colour is stated rather than inherited. It already computed to --body
   because the band has no colour modifier, so this changes nothing on screen
   -- but the distinction between the lead set and the paragraph below it was
   an accident of inheritance, and a band colour set later would have taken
   the lead prose with it and lost the distinction silently. Now the two are
   declared against each other and asserted. */
/* Six paragraphs in one 62ch column left the right half of a 1136px band
   empty -- the same defect fixed in the agent-case block and the intro block,
   and fixed the same way, with the same device: CSS columns, balanced, with
   break-inside so a paragraph cannot split mid-thought.

   Wrapped rather than applied to .sit-intro itself, because `columns` needs a
   container and these are six siblings. The wrapper is CSS-only; no markup
   changed, so the paragraphs are still six <p> in source order and a screen
   reader meets them in the order the client wrote them.

   The chips stay below the prose and are not moved up under the heading. The
   sixth paragraph says "any of the BELOW situations" -- moving the list above
   it would make the client's own sentence false. */
@media (min-width: 1024px) {
  .sit-intros { columns: 2; column-gap: var(--s-8); }
  .sit-intro { max-width: none; break-inside: avoid; }
}

/* ==========================================================================
   THE SITUATIONS TICK LIST
   Seven items, one per line, a bare tick on a fixed rail.

   It replaces two components that sat one above the other and carried the same
   circumstances twice: a ruled list of eight bare terms and a paragraph that
   said the same things in sentences. One list now does both jobs -- the term
   and the explanation on one line -- which is what the client asked for and
   what removes the duplication those two had between them.

   No rules between items and nothing behind the tick. The ruled treatment
   belongs to the any-condition list, which is bare terms and needs an edge to
   line up on; these items are sentences and carry their own extent.

   THE RAIL is a fixed 22px track, not auto, so every tick sits on the same
   vertical line whatever the item says. align-items: start plus 3px keeps the
   tick on the cap height of the FIRST line -- a three-line item must not push
   its mark to its own middle, and centring would.

   THE GAP between items is --s-6, 32px, against 25.5px of leading inside an
   item. Items have to be further apart than their own lines or the list reads
   as one stacked paragraph.

   TWO COLUMNS at 1024 by rows-plus-column-flow, not by columns-plus-row-flow:
   four explicit rows and grid-auto-flow: column fills 1-2-3-4 down the left
   and 5-6-7 down the right. The obvious spelling -- grid-template-columns with
   the default row flow -- reads 1-2 across, 3-4 across, and breaks the order
   the client wrote. grid-auto-columns keeps the two even.
   ========================================================================== */
.sit-tick__lead { max-width: 62ch; margin: 0 0 var(--s-5); font-size: var(--t-0); color: var(--ink); }
.sit-tick { list-style: none; margin: 0 0 var(--s-8); padding: 0; display: grid; gap: var(--s-6); }
.sit-tick li {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  gap: var(--s-3);
  align-items: start;
  font-size: var(--t-0);
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink);
}
.sit-tick__mark { display: block; margin-top: 3px; color: var(--primary); }
.sit-tick__mark svg { display: block; }
@media (min-width: 1024px) {
  .sit-tick {
    grid-template-rows: repeat(4, auto);
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    column-gap: var(--s-8);
  }
}

.prose-2 { display: grid; gap: var(--s-5); }
.prose-2 p { max-width: 46ch; font-size: var(--t-0); color: var(--ink); }
/* The emotional close of the section, not a leftover paragraph. A teal rule
   and one step up in size make it read as the last word rather than the
   last item. */
.sit-close {
  margin: var(--s-8) 0 0;
  padding-left: var(--s-5);
  border-left: 3px solid var(--primary);
  max-width: 52ch;
  font-size: var(--t-2);
  line-height: 1.3;
  letter-spacing: var(--track-h3);
  color: var(--ink);
}

/* The enquiry button below the pull-quote.

   A lead-in line lived here for one chunk (commit 630efb57): --t-1, no rule,
   weight 400, with the wrapper's row-gap holding it 16px from the button and
   margin-top holding the pair 48px from the quote. It was removed on the
   client's decision and the row-gap went with it -- a gap that separates a
   thing from a thing that no longer exists is a dead declaration, not a
   harmless leftover. margin-top: 48px stays; it is the only gap left and it
   already read, last chunk, as a distance that registers as separation
   rather than a hair's width.

   Column flex with align-items: flex-start, not block flow, because .btn is
   inline-flex. As a flex ITEM its margin box is placed directly against the
   wrapper's own margin-top; left in flow it would sit inside a line box whose
   leading would add itself to that 48 and the gap would no longer be the
   number in this file.

   THE RAIL is the pull-quote's own left edge -- where its teal border sits.
   That edge is the rail the heading, the six lead paragraphs and all seven
   ticks already stand on.

   No entrance animation: .sit-close has none, and a button that fades in is
   a button that is briefly not there. */
.sit-cta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  margin-top: var(--s-7);
}
@media (min-width: 860px) { .prose-2 { grid-template-columns: 1fr 1fr; gap: var(--s-8); } }


/* The homepage intro. A modifier on .prose-2, and it has to sit AFTER the rule
   above rather than beside the base one: both are a single class, so at 860+
   they tie on specificity and source order decides. Placed here for that
   reason and no other.

   Two things it changes. The gap, because this block joins the trust ribbon's
   band instead of getting one of its own -- see the note in front-page.php --
   so the separation a band would have given it has to come from somewhere.

   And the columns, off. .prose-2 splits two paragraphs into two columns and
   that is right where they are a matched pair, which is how the situations
   band uses it. Here the client's second paragraph is 20 words against the
   first's 70: split, the right column ran three lines and left a 300px empty
   rectangle beside it, which reads as a component that failed to load rather
   than as prose. One column, and .prose-2 p's own 46ch measure keeps it to a
   lede rather than a line running the full 1200px wrap. */
.homeintro { margin-top: var(--s-7); }
/* The grid gap alone, not the gap plus a paragraph margin. Measured before this
   line existed: 24px of gap and 16px of inherited margin-bottom put 40px
   between the two paragraphs against 32px between the ribbon and the first one,
   so the pair looked less related to each other than the first one looked to
   the icons above it. Now 48 above, 24 between: the break that matters is the
   larger one. */
.homeintro p { margin: 0; }
@media (min-width: 860px) {
  .homeintro { grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
}

/* Two columns from 860, and NOT the 1fr 1fr the base rule gives .prose-2.

   Equal columns were tried in chunk 16 and rejected on measurement: the
   client's second paragraph is 22 words against the first's 66, so at 1fr 1fr
   the right column ran three lines and left a 300px empty rectangle. One
   column was the fix for that and it created a worse one -- 435px of text in
   1136px of band, 38% of the width used and the whole right half empty.

   3fr 2fr with the closing sentence stepped up solves both at once. The
   narrower column plus the larger type makes the short paragraph nearly as
   tall as the long one (measured: 138px against 131px at 1280), so the
   columns balance without a word moving, and the block fills the band.

   The step up is also the answer to "does this need a heading". It has no
   heading and no eyebrow, which is what made it read as loose text; giving
   the concluding sentence one size and a teal rule builds that hierarchy out
   of the copy that exists rather than copy that would have to be written.

   The rule is .sit-close's, already on this page for the same job -- the last
   word of a section, one step up behind a teal edge. Reused, not invented.

   Below 860 nothing changes at all: one column, both paragraphs at body size,
   the phone layout exactly as it shipped. */
@media (min-width: 860px) {
  .homeintro {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    gap: var(--s-8);
    align-items: start;
  }
  .homeintro p { max-width: none; }
  .homeintro p:last-child {
    padding-left: var(--s-5);
    border-left: 3px solid var(--primary);
    font-size: var(--t-2);
    line-height: 1.3;
    letter-spacing: var(--track-h3);
  }
}

.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); list-style: none; margin: 0; padding: 0; }
.chips li {
  padding: var(--s-2) var(--s-4);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-size: var(--t--1);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  color: var(--ink);
}
.chips--onink li { background: transparent; border-color: var(--line-dark); color: var(--white); }
.chips li.is-lead { background: var(--primary-12); border-color: var(--primary); color: var(--hover); }

/* Areas are tiered: the ten boroughs lead, the places inside them follow. */
.chip-groups { margin: 0; }
.chip-groups dt {
  margin-bottom: var(--s-3);
  font-size: var(--t--1);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--on-ink);
}
.chip-groups dd { margin: 0 0 var(--s-5); }
.chip-groups dd:last-of-type { margin-bottom: 0; }
/* The ten boroughs are the local signal; the areas inside them support it.
   They were near-identical, and the supporting list was the bolder of the
   two. Boroughs now carry the weight, a lit border and white text; areas
   drop a step in size and lose their fill. */
.chips--major li {
  background: rgba(255, 255, 255, .09);
  border-color: rgba(255, 255, 255, .30);
  color: var(--white);
  font-weight: 700;
  padding: 0;
}
/* Ten borough names on a page about selling houses in Manchester used to
   link to nothing. Until borough pages exist they go to the postcode field,
   so recognising your own borough is one tap from using it. The anchor
   carries the padding so the whole chip is the target. */
.chips--major li a {
  display: block;
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-pill);
  color: inherit;
  text-decoration: none;
}
.chips--major li a:hover { color: var(--white); }
.chips--major li a:focus-visible {
  outline: 3px solid var(--primary);
  outline-offset: 2px;
}
.chips--minor li {
  background: transparent;
  border-color: var(--line-dark);
  color: var(--on-ink);
  font-size: var(--t--1);
  font-weight: 400;
}

/* ==========================================================================
   14. FAQ
   ========================================================================== */

/* Full band width rather than 46rem. A two-column arrangement was the
   alternative, but it would have put a one-line heading alone in a 40%
   column -- the same empty half, moved. The answers keep a reading measure
   of their own below, which is where the width actually matters. */
#questions .faq { max-width: none; }
.faq { max-width: 46rem; }
.faq details { border-top: 1px solid var(--line); }
.faq details:last-of-type { border-bottom: 1px solid var(--line); }
.faq summary {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: 44px;
  padding-block: var(--s-5);
  font-size: var(--t-1);
  font-weight: 700;
  line-height: 1.24;
  letter-spacing: var(--track-tight);
  color: var(--ink);
  cursor: pointer;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  flex: 0 0 auto;
  width: 14px; height: 14px;
  margin-top: 6px;
  border-right: 2px solid var(--hover);
  border-bottom: 2px solid var(--hover);
  transform: rotate(45deg);
  transition: transform var(--ease);
}
.faq details[open] summary::after { transform: rotate(-135deg); }
.faq summary:hover { color: var(--hover); }
.faq__body { padding-bottom: var(--s-5); max-width: var(--measure); }
/* Tighter rhythm, homepage only: eight questions is a long stack there. */
#questions .faq summary { padding-block: var(--s-4); }
/* Eight summaries at --t-1 made a collapsed stack nearly two screens tall
   on a phone. At --t-0 they are still bold and still chevroned, and the
   44px floor is untouched. */
@media (max-width: 1023.98px) {
  #questions .faq summary { font-size: var(--t-0); }
}
/* #questions, not .faq. The situation pages render .faq from page content on
   an ink band, where --ink answers would be navy on navy. */
#questions .faq__body p { color: var(--ink); }

/* The FAQ sits on an ink band on situation pages. Everything in .faq is
   coloured for a light background, so restate it here rather than leaving
   navy-on-navy questions and a deep-teal chevron nobody can see. */
.band--ink .faq details        { border-color: var(--line-dark); }
.band--ink .faq summary        { color: var(--white); }
.band--ink .faq summary:hover  { color: var(--primary); }
.band--ink .faq summary::after { border-right-color: var(--primary); border-bottom-color: var(--primary); }

/* An emphasis block inside a prose band. Used to break a long single band and
   to carry the one answer on a page that must not be skim-read past — the
   sale-and-rent-back answer on the repossession page. Not a new band. */
/* Body prose in a white card on a dark band. Opt-in per page via
   <!--shf:card-->; without it the reading column below applies as normal.

   WIDTH. 58rem / 928px, centred. Padding is clamp(--s-5, 5vw, --s-9), so 96px
   a side on desktop and 24px on a phone. 928 - 192 = 736px of inner width,
   which lands within a few pixels of --measure (62ch). The card is wide; the
   line length inside it is not, because the padding is doing the work. That is
   the whole trick — a 930px card with 24px padding would be 880px of prose and
   unreadable.

   CORNERS AND SHADOW. --r-lg (16px), no border, no shadow. The theme has two
   card conventions already — .card is border + --r-lg, .plan is shadow +
   --r-lg — and both are rounded, so square corners would be the only square
   surface on the site and would read as borrowed from somewhere else. A border
   is dropped because a light hairline on navy reads as a rendering artefact,
   and a shadow is dropped because a dark shadow on a dark band is invisible.
   White on ink is a 14.5:1 edge; it needs no help.

   COLOUR. .band--ink sets colour on everything inside it, including headings,
   so the card has to put them back or the text goes white-on-white. */
.situation-card {
  max-width: 58rem;
  margin-inline: auto;
  padding: clamp(var(--s-5), 5vw, var(--s-9));
  background: var(--white);
  border-radius: var(--r-lg);
  color: var(--body);
}
.situation-card .situation-body { max-width: none; }
.situation-card :is(h1, h2, h3, h4) { color: var(--ink); }
.situation-card p,
.situation-card li { color: var(--body); }
.situation-card strong { color: var(--ink); }
.situation-card a { color: var(--hover); }
@media (max-width: 559px) {
  /* Inset, not full-bleed. A full-bleed card on a phone is just a white page
     with a navy strip top and bottom — the card stops being an object. Keeping
     the wrap gutter either side preserves the dark ground it sits on, which is
     the only thing making it read as a card at that width. */
  .situation-card { border-radius: var(--r-md); }
}

/* The reading column, left-aligned to the wrap.

   The situation template used to put every prose band in .wrap--narrow, a 680px
   container that centres itself. The four full-width bands around them — hero,
   both postcode blocks, call band — use the 1200px .wrap. At 1568px that gave
   the page two competing left edges, x=208 and x=468, alternating seven times
   down the page: the eye was dragged 260px sideways at every band change. The
   column was correctly measured and correctly centred; it was centred against
   the viewport while everything else aligned to the wrap.

   The fix is the pattern the homepage already uses. .band-head is `max-width:
   40ch` and .prose-2 p is `max-width: 46ch` — a measure applied to a
   left-aligned block inside the full wrap, not a narrower container centred
   inside it. Text starts at the same x on every band and simply stops early.

   38.5rem is the 616px the column already measured, so no page re-flows and no
   line length changes. It is a max-width, so narrow viewports are unaffected. */
.situation-body { max-width: 38.5rem; }

.situation-body .callout {
  margin-block: var(--s-7);
  padding: var(--s-5) var(--s-6);
  background: var(--surface);
  border-left: 3px solid var(--primary);
  border-radius: 0 var(--r-md) var(--r-md) 0;
}
.situation-body .callout > :last-child { margin-bottom: 0; }

/* Optional icon anchoring a callout. One callout per situation page carries the
   single sentence a skim-reader must not miss, and the icon is what makes it
   findable at a glance rather than another indented paragraph.

   Colour is --hover (#00806F), not --primary. #00BFA6 on --surface measures
   1.9:1 and fails the 3:1 that WCAG 1.4.11 asks of a meaningful graphic; the
   darkened teal measures 4.58:1 on the same ground. The 3px left border may
   stay --primary because it is decoration and carries no meaning on its own. */
.situation-body .callout__icon {
  display: block;
  margin-bottom: var(--s-3);
  color: var(--hover);
}

/* A ticked qualifying list inside page content.

   Same construction as .compare__list on the homepage — a fixed icon rail and
   a hairline between rows — but available to authored content, where the
   homepage component is not. The icon is a real inline SVG from the registry,
   so the list costs no request and the ticks are not text characters that a
   screen reader would read out one per line.

   The <ul> carries .ticklist and each <li> opens with an [shf_icon name="check"]
   shortcode. Marker colour is --hover for the same contrast reason as the
   callout icon. */
.situation-body .ticklist { list-style: none; margin: var(--s-5) 0; padding: 0; }
.situation-body .ticklist li {
  display: grid;
  grid-template-columns: 30px 1fr;
  gap: var(--s-4);
  padding-block: var(--s-3);
  border-top: 1px solid var(--line);
  align-items: start;
}
.situation-body .ticklist li::marker { content: none; }
.situation-body .ticklist li:first-child { border-top: 0; }

/* The marker is meant to be the first thing the eye lands on in each row —
   this list is a recognition exercise, not decoration, and a reader is running
   down it looking for their own house.

   Weight comes from a pale teal disc behind the glyph rather than from a
   brighter glyph. --primary at 14% is a ground, not a graphic, so it carries no
   contrast requirement; the tick itself stays --hover (#00806F), which measures
   about 4.9:1 on white. Setting the check to --primary instead would have been
   2.0:1 and failed the 3:1 WCAG 1.4.11 asks of a meaningful graphic — the
   obvious teal is the wrong teal here. */
.situation-body .ticklist svg {
  color: var(--hover);
  background: rgba(0, 191, 166, .14);
  border-radius: 50%;
  padding: 5px;
  box-sizing: content-box;
  margin-top: 1px;
}

/* The process flow, borrowed onto a situation page.

   .flow goes four-across at 1024px, and the situation template gives everything
   the 680px reading measure — four columns in 616px is 154px each, which is not
   a column, it is a stack pretending. This bleeds it out to 872px at exactly
   the width its own layout switches, so the two breakpoints cannot disagree.

   Same technique as the card grid used, and the same warning: if .flow's own
   breakpoint moves, this one moves with it.

   The two resets below are not tidiness. .flow is an <ol>, and
   `.entry-content ul, .entry-content ol` sets padding-left and a --measure
   max-width. That selector is (0,1,1) and .flow's own reset is (0,1,0), so
   inside page content the list rules win: the flow came out capped at 732px
   with a 24px indent, which is four 153px columns — the exact stack-pretending
   -to-be-a-grid this bleed exists to prevent. */
.situation-body .flow { padding-left: 0; max-width: none; }
@media (min-width: 1024px) {
  .situation-body .flow {
    width: calc(100% + 2 * var(--s-10));
    max-width: none;
    /* Bleeds RIGHT only, not both ways. The column is left-aligned to the wrap
       now, so a symmetric bleed would push the flow out through the wrap's left
       edge and break the single left spine this layout exists to hold. There is
       spare width on the right and nowhere else, so that is where it goes. */
    margin-right: calc(-2 * var(--s-10));
  }
}

/* Situation list — the hub's index of situation pages.

   This replaced a .card grid. Three cards sat in a row, a fourth forced
   grid--2 and a 2 x 2, and a fifth would have orphaned on a third row: every
   addition was a layout decision. A stacked list has no column count to break,
   so it takes the sixth and seventh pages without another discussion. It also
   sits at the reading measure with the prose around it instead of bleeding
   wider, which is what the rest of the page does. */
.situation-list { list-style: none; margin: var(--s-6) 0 0; padding: 0; }
.situation-list > li {
  display: grid;
  /* Fixed 48px column, not `auto`. With `auto` the column tracked the icon and
     every row's text started in a very slightly different place; a fixed rail
     gives the set a left edge to read down. */
  grid-template-columns: 48px 1fr;
  gap: var(--s-3) var(--s-4);
  align-items: start;
  padding: var(--s-5) 0;
  border-top: 1px solid var(--line);
}
.situation-list > li:last-child { border-bottom: 1px solid var(--line); }

/* Place both cells explicitly, and drop empty paragraphs.

   wpautop runs over this content and inserts an empty <p></p> between the icon
   span and the body div. As a third grid item it took cell (1,2) and pushed the
   body to (2,1) — a 48px column — so every heading wrapped one word per line and
   each row rendered ~900px tall instead of ~150px. The page still "worked", which
   is why it survived a height measurement: 4430px looked like the honest cost of
   stacking five rows, and it was a layout bug.

   Explicit placement makes the row immune to whatever the autop pass leaves
   behind, and :empty removes the stray node from flow entirely. */
.situation-list > li > p:empty { display: none; }
.situation-list__icon { grid-column: 1; grid-row: 1; color: var(--hover); margin-top: 1px; }
.situation-list__body { grid-column: 2; grid-row: 1; }
.situation-list__body > :first-child { margin-top: 0; }
.situation-list__body > :last-child { margin-bottom: 0; }
.situation-list h3 { margin: 0 0 var(--s-2); }
/* The description steps down from body size so the heading is the entry point.
   Without this the row was three lines of identical weight and the eye had
   nowhere to land. */
.situation-list p { margin: 0 0 var(--s-2); font-size: var(--t--1); color: var(--body); }
.situation-list__body p:last-child { font-size: var(--t-0); color: inherit; }
@media (max-width: 479px) {
  /* The icon narrows rather than disappearing. An earlier version hid it below
     480px, which removed the theme's visual language from the width where most
     of this traffic actually is and left a plain list of five headings. 36px
     rail plus a 24px glyph still leaves ~295px of a 343px measure for text. */
  .situation-list > li { grid-template-columns: 36px 1fr; gap: var(--s-2) var(--s-3); }
  .situation-list__icon svg { width: 24px; height: 24px; }
}

/* Same autop guard for the ticked list: the shortcode and its <span> sit on one
   line so nothing should be inserted between them, but the row is a grid and one
   stray node would break it the same way. */
.situation-body .ticklist > li > p:empty { display: none; }

/* A card grid wants the band width; prose wants the 680px reading measure, and
   the situation template gives everything the measure. Three cards need 864px
   and were getting 616px, so the third wrapped onto its own row. This widens
   only the grid, and only where there is room for it: at 1100px the wrap's own
   content box is 1036px, so 872px never overflows.

   --2 is in this selector for the same reason, not for symmetry. Its columns
   have a 22rem floor, so two of them plus the gap need 728px; the unwidened
   measure gives 616px and auto-fit silently collapses to one column. The hub's
   fourth card landed exactly there — the wrapper had been switched to grid--2
   as the card-grid comment instructs, and the cards still stacked 4 x 1,
   because the widening was scoped to --3 alone. Change one, change the other.

   Nothing uses these two selectors today — the hub's card grid became
   ul.situation-list — but they are kept correct rather than deleted, because a
   grid in page content is a reasonable thing to want again. Bleeding RIGHT
   only, for the same reason as the flow: the column is left-aligned now. */
@media (min-width: 1100px) {
  .situation-body .grid--3,
  .situation-body .grid--2 {
    width: calc(100% + 2 * var(--s-10));
    margin-right: calc(-2 * var(--s-10));
  }
}


/* ==========================================================================
   15. CARDS
   ========================================================================== */

.card {
  display: flex;
  flex-direction: column;
  padding: var(--s-6);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.card h3 { margin-bottom: var(--s-3); }

.shf-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  margin-bottom: var(--s-4);
  border-radius: var(--r-md);
  background: var(--primary-12);
  color: var(--hover);
}

/* ==========================================================================
   16. CTA BAND
   ========================================================================== */

.shf-cta-band, .callband {
  background: var(--ink);
  color: var(--on-ink);
}
.shf-cta-band h2, .callband h2 { color: var(--white); }
.callband__inner { display: grid; gap: var(--s-6); }
@media (min-width: 900px) { .callband__inner { grid-template-columns: 1fr auto; align-items: center; } }
.shf-cta-band__inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-6); }
.shf-cta-band__text { flex: 1 1 24rem; }

/* ==========================================================================
   17. FORMS (step two + generic)
   ========================================================================== */

.form { display: grid; gap: var(--s-4); }
.plan .form { gap: var(--s-3); }
.plan .form__row { gap: var(--s-3); }
.form__row { display: grid; gap: var(--s-4); }
@media (min-width: 560px) {
  .form__row--2 { grid-template-columns: 1fr 1fr; }
  /* Subgrid, so the label row and the control row are shared by both
     fields rather than each field stacking independently. A label that
     wraps then grows the shared row and BOTH controls move together --
     they cannot come apart again, in any language. */
  @supports (grid-template-rows: subgrid) {
    .form__row--2 { grid-template-rows: auto auto auto; }
    .form__row--2 > .field { grid-row: span 3; grid-template-rows: subgrid; }
  }
}

.field { display: grid; gap: var(--s-1); }
.field > label { font-size: var(--t--1); font-weight: 700; letter-spacing: var(--track-tight); color: var(--ink); }
.field__hint { font-size: var(--t--1); color: var(--body); }
.field__error { font-size: var(--t--1); font-weight: 700; color: var(--error); }
.field--error input, .field--error select, .field--error textarea { border-color: var(--error); }
.req { color: var(--error); }

input[type="text"], input[type="email"], input[type="tel"], input[type="search"],
input[type="url"], input[type="number"], textarea, select {
  width: 100%;
  min-height: 48px;
  padding: var(--s-3) var(--s-4);
  background: var(--white);
  border: 2px solid var(--field-line);
  border-radius: var(--r-md);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-0);
  line-height: 1.4;
  transition: border-color var(--ease);
}
textarea { min-height: 120px; resize: vertical; }
/* Four fields at four heights -- 55, 52, 52 and 60 -- because min-height
   plus vertical padding plus a select's own line box do not agree. A fixed
   height and zero vertical padding make them agree: the text centres on
   the line box instead of being pushed by padding. 52px clears the 44px
   floor at every width. */
.form .field > input,
.form .field > select {
  height: 52px;
  min-height: 52px;
  padding-block: 0;
}
/* The select was reading as a bolted-on component: same border, same
   radius, but 64px of right padding against the inputs' 16px and its own
   line box. Its chevron needs the room, so the room is all it gets. */
/* 48px of chevron room in a 169px half-row left 101px for text and the
   default option needs 111, which is why it rendered "Rather not sa".
   The field has the full row now and the chevron keeps 32px. The widest
   option, "Repossession or mortgage arrears", measures 262px. */
.form .field > select {
  padding-right: var(--s-6);
  /* A native select clips its own text rather than overflowing, so
     scrollWidth never reports it -- this has to be measured by rendering
     the option string. Ellipsis makes the residue at 360 deliberate. */
  text-overflow: ellipsis;
}
@media (max-width: 1023.98px) {
  /* 20px bought back from the padding. Clears the longest option at 390;
     at 360 it is still 28px over and ellipsises, because the only other
     fixes are shortening the option text or going under the 16px iOS
     zoom threshold, and both are worse. */
  .form .field > select { padding-left: var(--s-3); padding-right: 28px; }
  .form .field > select { background-position: right var(--s-3) center; }
}
select {
  appearance: none;
  padding-right: var(--s-8);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235F6B70' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s-4) center;
  background-size: 16px;
}
/* Hover/mouse focus tightens the border. The ring itself is left to the global
   :focus-visible rule — do NOT add `outline: none` here, it outranks it. */
input:focus, textarea:focus, select:focus {
  border-color: var(--ink);
}
::placeholder { color: #8D9AA0; opacity: 1; }

/* Privacy notice. Still a plain statement rather than a gate, but collapsed:
   as an always-open paragraph it occupied 226px on a phone — more than the two
   required fields it explains. Open by default would be honest but unusable;
   <details> keeps it one tap away, in the DOM, and working with JS off. */
/* The one optional question, behind one tap. Same CSS-only disclosure the
   card, the FAQ and the footer already use. */
.whyblock { border-top: 1px solid var(--line); padding-top: var(--s-3); }
.whyblock__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: 44px;
  cursor: pointer;
  list-style: none;
  font-size: var(--t--1);
  font-weight: 700;
  color: var(--ink);
}
.whyblock__summary::-webkit-details-marker { display: none; }
.whyblock__summary::after {
  content: "";
  width: 10px;
  height: 10px;
  border-right: 2px solid var(--hover);
  border-bottom: 2px solid var(--hover);
  transform: rotate(45deg);
}
.whyblock[open] .whyblock__summary::after { transform: rotate(-135deg); }
.whyblock__body { padding-top: var(--s-3); }

.privacy-note {
  background: var(--surface);
  border-radius: var(--r-md);
  font-size: var(--t--1);
  line-height: 1.55;
}
.privacy-note summary {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  padding: var(--s-2) var(--s-4);
  font-weight: 700;
  color: var(--ink);
  cursor: pointer;
  list-style: none;
}
.privacy-note summary::-webkit-details-marker { display: none; }
.privacy-note summary::before {
  content: "";
  flex: 0 0 auto;
  width: 9px; height: 9px;
  border-right: 2px solid var(--hover);
  border-bottom: 2px solid var(--hover);
  transform: rotate(-45deg);
  transition: transform var(--ease);
}
.privacy-note[open] summary::before { transform: rotate(45deg); }
.privacy-note__body { padding: 0 var(--s-4) var(--s-4); }

/* Optional marketing opt-in — genuinely optional, never required. */
.optin {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-3);
  align-items: center;
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}
.optin input[type="checkbox"] {
  width: 28px; height: 28px;
  min-height: 0; margin: 0;
  accent-color: var(--hover);
  cursor: pointer;
}
.optin label { display: flex; align-items: center; min-height: 44px; font-size: var(--t--1); line-height: 1.4; cursor: pointer; }

/* Honeypot */
.hp { position: absolute !important; left: -9999px !important; width: 1px; height: 1px; overflow: hidden; }
.shf-hp { position: absolute !important; left: -9999px !important; width: 1px; height: 1px; overflow: hidden; }

.alert {
  display: grid;
  gap: var(--s-2);
  padding: var(--s-4) var(--s-5);
  border-left: 3px solid;
  border-radius: 0 var(--r-md) var(--r-md) 0;
  font-size: var(--t--1);
  line-height: 1.5;
}
.alert strong { font-size: var(--t-0); }
.alert--ok  { background: rgba(0,128,111,.08); border-color: var(--hover); color: #044F44; }
.alert--bad { background: rgba(179,38,30,.07); border-color: var(--error); color: #8C1E17; }
.alert ul { margin: 0; padding-left: var(--s-4); }

/* ==========================================================================
   18. BLOG (kept from v1, retuned to the new scale)
   ========================================================================== */


/* Situation hero carrying the enquiry card.

   Opt-in per page via <!--shf:hero-form-->; without it the hero is a single
   block and this never applies. Words first in source order, so a phone reads
   H1 then promise then form — the card must not come before the heading it is
   answering. Two columns only from 960px, where 1fr/26rem still leaves the H1 a
   sensible measure.

   The hero texture sits behind both columns and is hidden below 768px anyway,
   so it never collides with the card. */
@media (min-width: 960px) {
  .page-hero__grid {
    grid-template-columns: 1fr 26rem;
    align-items: start;
    gap: var(--s-8);
  }
}

/* Below 768px the hero card collapses to the ask.

   With the full card the hero measured 1219px on a 375x812 phone — one and a
   half screens of ink before a single line of argument. .plan__body carries two
   timeline rows and .plan__caption the note beneath them, and on this page both
   are said again by the full shf_flow() further down. Losing them costs the
   reader nothing and buys back most of a screen.

   Scoped to .page-hero__grid, so the homepage hero card is untouched — there
   the timeline rows are the only dated content above the fold. */
@media (max-width: 767px) {
  .page-hero__grid .plan__body,
  .page-hero__grid .plan__caption { display: none; }
  .page-hero__grid .plan__top { border-bottom: 0; }
}
/* Hero texture. See the comment in template-situation.php for why this is a
   field and not a drawing.

   Below 768px it is display:none rather than faded further: the fold is tight
   there and even a 7% field is one more thing between the H1 and the form. */
.page-hero__texture {
  position: absolute;
  right: -3rem;
  top: 50%;
  transform: translateY(-50%);
  width: 22rem;
  height: 22rem;
  color: currentColor;
  opacity: .07;
  pointer-events: none;
}
@media (max-width: 767px) {
  .page-hero__texture { display: none; }
}

/* The page hero: ground, ink and rhythm, for every template that opens with
   one -- situation, page, single, contact, archive, search and 404.

   position/overflow are here to contain the texture field. The <svg> is
   absolutely positioned and pointer-events:none, so it takes no space and
   cannot be hit; the hero's height is set entirely by the H1 and the promise
   line. Those two declarations sat in a block of their own 51 lines up until
   chunk 7 -- same component, same specificity, no overlapping properties and
   nothing anywhere saying the other half existed. The region lint could not
   see it either until it was taught to read the subject of a selector. */
.shf-page-hero {
  position: relative;
  overflow: hidden;
  background: var(--ink);
  color: var(--on-ink);
  padding-block: clamp(var(--s-6), 5vw, var(--s-8));
}
.shf-page-hero h1 { color: var(--white); margin-bottom: var(--s-3); }
.shf-page-hero p { font-size: var(--t-1); line-height: 1.42; letter-spacing: var(--track-tight); max-width: 46ch; margin-bottom: 0; }

/* The hub, and only the hub. 5vw tops the hero padding out at 64px a side on
   a desktop, which put the first card below the fold at 1280 before a single
   row had been drawn. The H1 drops another step with it: this is a hub
   heading standing above a grid, not a hero statement, and two lines of 41px
   type cost more than they were earning. Scoped with :has(), so the eleven
   other situation pages keep the full-size hero. */
body:has(.reasons) .shf-page-hero { padding-block: var(--s-5); }
body:has(.reasons) .shf-page-hero h1 { font-size: var(--t-3); }
/* The sub-line follows the heading down. Dropping the H1 to --t-3 without this
   left 33.2px over 20px, an interval of 1.65:1 where it had been 2:1, and the
   two started reading as one block rather than a statement and its qualifier. */
body:has(.reasons) .shf-page-hero p { font-size: var(--t-0); }

/* Two lines below the 768 rail. The promise line runs to three lines at 360,
   and its second half says what "Everything else" says again further down the
   same page -- the reader meets it twice inside one scroll. Clamped rather than
   cut, because the full sentence is still the right one on a wide screen and
   the copy is not this chunk's to rewrite. */
@media (max-width: 767px) {
  .page-template-template-situation .shf-page-hero p {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
  }
}

.shf-breadcrumbs { margin-bottom: var(--s-4); font-size: var(--t--1); color: var(--on-ink-quiet); }
.shf-breadcrumbs a { color: var(--primary); text-decoration: none; }
.shf-breadcrumbs a:hover { text-decoration: underline; }
.shf-breadcrumbs span[aria-hidden] { padding-inline: var(--s-2); opacity: .6; }

/* Out of the situation hero, and only that hero.

   shf_breadcrumbs() prints the <nav> AND calls shf_breadcrumb_schema(), which
   emits the BreadcrumbList JSON-LD that Google reads for the trail under a
   search result. Deleting the call from template-situation.php would have
   taken the structured data with it on all twelve situation pages, so the
   trail is hidden and the schema left alone.

   Visually hidden rather than display:none, so the nav stays in the
   accessibility tree and is still a landmark.

   Not sitewide. On a blog post the trail is three levels and carries real
   hierarchy; page, contact, search and 404 keep theirs too. On a situation
   page it is two levels and the second is the H1 immediately beneath it. */
.page-template-template-situation .shf-page-hero .shf-breadcrumbs {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

.shf-posts { display: grid; gap: var(--s-5); grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr)); }
.shf-post-card { display: flex; flex-direction: column; overflow: hidden; background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); transition: border-color var(--ease); }
.shf-post-card:hover { border-color: var(--primary); }
.shf-post-card__media { display: block; aspect-ratio: 16 / 10; background: var(--surface); overflow: hidden; }
.shf-post-card__media img { width: 100%; height: 100%; object-fit: cover; }
.shf-post-card__media--empty { display: grid; place-items: center; background: var(--ink); color: var(--primary); font-size: var(--t-1); font-weight: 700; letter-spacing: var(--track-h3); }
.shf-post-card__body { display: flex; flex-direction: column; gap: var(--s-3); padding: var(--s-5); flex: 1; }
.shf-post-card__title { font-size: var(--t-1); margin: 0; line-height: 1.2; letter-spacing: var(--track-tight); }
.shf-post-card__title a { color: var(--ink); text-decoration: none; }
.shf-post-card__title a:hover { color: var(--hover); }
.shf-post-card__excerpt { font-size: var(--t--1); margin: 0; }
.shf-post-card__foot { margin-top: auto; }

.shf-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-4); font-size: var(--t--1); color: var(--body); }
.shf-meta a { color: inherit; }
/* Post meta sitting on an ink field. Was a hard-coded rgba() in single.php,
   which bypassed the tokens entirely. Measured: 6.89:1 on ink. */
.shf-meta--on-ink { color: var(--on-ink-quiet); }

/* Contact page: content beside a fixed-width enquiry column. Was an inline
   grid-template-columns override on .shf-blog-layout. */
.contact-layout { display: grid; gap: clamp(var(--s-6), 5vw, var(--s-8)); align-items: start; }
@media (min-width: 1000px) { .contact-layout { grid-template-columns: minmax(0, 1fr) minmax(0, 26rem); } }
.shf-meta__cat { position: relative; }
.shf-meta__cat::after { content: ""; position: absolute; inset: -9px -6px; }
.shf-meta__cat { display: inline-block; padding: 2px var(--s-2); background: var(--primary-12); border-radius: var(--r-pill); color: var(--hover); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; text-decoration: none; }

.shf-blog-layout { display: grid; gap: clamp(var(--s-6), 5vw, var(--s-8)); align-items: start; }
@media (min-width: 1000px) { .shf-blog-layout { grid-template-columns: minmax(0, 1fr) 19rem; } }
.shf-sidebar { display: grid; gap: var(--s-5); position: sticky; top: var(--s-9); }
.shf-widget { padding: var(--s-5); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); }
.shf-widget__title { font-size: var(--t-1); margin-bottom: var(--s-4); }
.shf-widget ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
.shf-widget li a { color: var(--ink); font-size: var(--t--1); font-weight: 700; text-decoration: none; }
.shf-widget li a:hover { color: var(--hover); }
.shf-widget--cta { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
.shf-widget--cta .shf-widget__title { color: var(--white); }

.shf-article__featured { margin: 0 0 var(--s-6); border-radius: var(--r-lg); overflow: hidden; }
.shf-article__featured img { width: 100%; }

.entry-content { font-size: var(--t-1); line-height: 1.6; }
.entry-content > * + * { margin-top: var(--s-5); }
.entry-content > h2 { margin-top: var(--s-8); }
.entry-content > h3 { margin-top: var(--s-7); }
.entry-content > *:first-child { margin-top: 0; }
.entry-content p { max-width: var(--measure); }
.entry-content ul, .entry-content ol { padding-left: var(--s-5); max-width: var(--measure); }
.entry-content li { margin-bottom: var(--s-2); }
.entry-content li::marker { color: var(--hover); }
.entry-content a { color: var(--hover); font-weight: 700; }
.entry-content a:hover { color: var(--ink); }
.entry-content img { border-radius: var(--r-md); }
.entry-content figure { margin: var(--s-6) 0; }
.entry-content figcaption, .entry-content .wp-caption-text { margin-top: var(--s-2); font-size: var(--t--1); color: var(--body); }

.entry-content .alignleft,  .entry-content .wp-caption.alignleft  { float: left;  margin: var(--s-1) var(--s-5) var(--s-4) 0; max-width: 50%; }
.entry-content .alignright, .entry-content .wp-caption.alignright { float: right; margin: var(--s-1) 0 var(--s-4) var(--s-5); max-width: 50%; }
.entry-content .aligncenter, .entry-content .wp-caption.aligncenter { margin-inline: auto; text-align: center; }
.entry-content .alignwide { width: min(100vw - 2 * var(--gutter), 58rem); margin-inline: auto; max-width: none; }
.entry-content .alignfull { width: 100vw; max-width: none; margin-inline: calc(50% - 50vw); }
.entry-content .alignfull img { border-radius: 0; }
@media (max-width: 640px) {
  .entry-content .alignleft, .entry-content .alignright,
  .entry-content .wp-caption.alignleft, .entry-content .wp-caption.alignright { float: none; margin-inline: auto; max-width: 100%; }
}

.entry-content :is(table, .wp-block-table) { display: block; overflow-x: auto; }
.entry-content table { width: 100%; border-collapse: collapse; font-size: var(--t-0); }
.entry-content th, .entry-content td { padding: var(--s-3) var(--s-4); border: 1px solid var(--line); text-align: left; }
.entry-content thead th { background: var(--surface); color: var(--ink); }
.entry-content .wp-block-button__link { display: inline-block; padding: var(--s-3) var(--s-5); background: var(--ink); color: var(--white); border-radius: var(--r-md); font-weight: 700; text-decoration: none; }

.shf-inline-cta {
  margin: var(--s-8) 0;
  padding: var(--s-6);
  background: var(--surface);
  border-left: 3px solid var(--primary);
  border-radius: 0 var(--r-lg) var(--r-lg) 0;
  font-size: var(--t-0);
}
.shf-inline-cta h3 { margin-bottom: var(--s-2); }

.shf-toc { padding: var(--s-5); margin-bottom: var(--s-6); background: var(--surface); border-radius: var(--r-lg); font-size: var(--t--1); }
.shf-toc__title { font-size: var(--t-0); margin-bottom: var(--s-3); }
.shf-toc ol { margin: 0; padding-left: var(--s-4); display: grid; gap: var(--s-1); }
.shf-toc a { color: var(--ink); text-decoration: none; font-weight: 700; }
.shf-toc a:hover { color: var(--hover); text-decoration: underline; }

.shf-tags { display: flex; flex-wrap: wrap; gap: var(--s-2); list-style: none; margin: 0; padding: 0; }
.shf-tags a { display: inline-block; padding: var(--s-1) var(--s-3); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); font-size: var(--t--1); font-weight: 700; color: var(--ink); text-decoration: none; }

.shf-post-nav { display: grid; gap: var(--s-4); margin-top: var(--s-7); padding-top: var(--s-6); border-top: 1px solid var(--line); }
@media (min-width: 700px) { .shf-post-nav { grid-template-columns: 1fr 1fr; } }
.shf-post-nav a { display: block; padding: var(--s-4) var(--s-5); border: 1px solid var(--line); border-radius: var(--r-md); text-decoration: none; }
.shf-post-nav a:hover { border-color: var(--primary); }
.shf-post-nav small { display: block; margin-bottom: var(--s-1); font-size: var(--t--1); font-weight: 700; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--hover); }
.shf-post-nav span { display: block; font-size: var(--t-1); font-weight: 700; line-height: 1.2; letter-spacing: var(--track-tight); color: var(--ink); }
.shf-post-nav .is-next { text-align: right; }

.shf-pagination { margin-top: var(--s-7); }
.shf-pagination .nav-links { display: flex; flex-wrap: wrap; gap: var(--s-2); justify-content: center; }
.shf-pagination .page-numbers { display: inline-flex; align-items: center; justify-content: center; min-width: 48px; height: 48px; padding-inline: var(--s-3); border: 1px solid var(--line); border-radius: var(--r-md); color: var(--ink); font-weight: 700; text-decoration: none; }
.shf-pagination .page-numbers:hover { border-color: var(--primary); color: var(--hover); }
.shf-pagination .page-numbers.current { background: var(--ink); border-color: var(--ink); color: var(--white); }

.shf-comments { margin-top: var(--s-8); padding-top: var(--s-7); border-top: 1px solid var(--line); }
.shf-comments ol { list-style: none; margin: 0; padding: 0; }
.shf-comments .children { list-style: none; margin: var(--s-4) 0 0; padding-left: clamp(var(--s-4), 4vw, var(--s-6)); }
.shf-comments article { padding: var(--s-5); margin-bottom: var(--s-4); background: var(--surface); border-radius: var(--r-md); }
.shf-comments .comment-author { display: flex; align-items: center; gap: var(--s-3); font-weight: 700; color: var(--ink); }
.shf-comments .comment-author img { border-radius: 50%; }
.shf-comments .comment-metadata { margin-bottom: var(--s-3); font-size: var(--t--1); color: var(--body); }


/* ==========================================================================
   CORE BLOCK WIDGETS
   WordPress auto-populates the first registered sidebar with Search, Recent
   Posts and Recent Comments block widgets on theme activation. That makes
   is_active_sidebar() true, so the theme's own designed sidebar never renders
   and the blog pages show unstyled core markup instead. Whatever the client
   ends up putting in there has to look like it belongs.
   ========================================================================== */

.shf-sidebar .wp-block-group,
.shf-sidebar > .widget_block {
  padding: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.shf-sidebar .wp-block-heading,
.shf-sidebar .wp-block-group h2 {
  margin-bottom: var(--s-4);
  font-size: var(--t-1);
  line-height: 1.2;
  letter-spacing: var(--track-tight);
}
.shf-sidebar ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); }
.shf-sidebar li { font-size: var(--t--1); line-height: 1.4; }
.shf-sidebar li a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--ink);
  font-weight: 700;
  text-decoration: none;
}
.shf-sidebar li a:hover { color: var(--hover); }

/* Core search block — its default button is 21px tall. */
.wp-block-search__inside-wrapper { display: flex; gap: var(--s-2); }
.wp-block-search__input {
  flex: 1 1 auto;
  min-height: 48px;
  padding: var(--s-3) var(--s-4);
  border: 2px solid var(--field-line);
  border-radius: var(--r-md);
  font-family: var(--font);
  font-size: var(--t-0);
}
.wp-block-search__button {
  min-height: 48px;
  padding-inline: var(--s-4);
  background: var(--ink);
  border: 0;
  border-radius: var(--r-md);
  color: var(--white);
  font-family: var(--font);
  font-size: var(--t--1);
  font-weight: 700;
  cursor: pointer;
}
.wp-block-search__button:hover { background: #16405A; }

.wp-block-latest-comments { padding-left: 0; }
.wp-block-latest-comments__comment { font-size: var(--t--1); line-height: 1.45; margin-bottom: var(--s-3); }
.wp-block-latest-comments__comment-meta { font-weight: 700; }

/* ==========================================================================
   19. FOOTER
   ========================================================================== */

.shf-footer { background: var(--ink); color: var(--on-ink); font-size: var(--t--1); }
.shf-footer a { color: var(--on-ink); text-decoration: none; }
.shf-footer a:hover { color: var(--primary); text-decoration: underline; }
.shf-footer__main { display: grid; gap: var(--s-6); padding-block: var(--s-8) var(--s-7); grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); }
.shf-footer__brand { grid-column: 1 / -1; }
@media (min-width: 900px) { .shf-footer__brand { grid-column: auto; max-width: 24rem; } }
/* Four equal columns with wildly unequal content is why the last one looked
   abandoned. The brand column gets the width its two paragraphs need and
   the three link columns get what a list of short links needs. */
@media (min-width: 1024px) {
  /* Five groups now, so the brand keeps its width and the four lists share
     the rest evenly. Areas is two sub-columns of ten short names, which
     makes it about as tall as a six-link list. */
  /* Four tracks, five groups. Areas and Latest guides were the two short
     columns -- 183px and 173px against the brand's 387 -- so they share
     one cell and stack. Their pair comes to about the brand's height,
     which is what stops the row reading as columns that ran out. */
  /* Three lists now that Areas has gone. Why people sell gets the widest
     of them because its labels are full descriptive page titles and the
     brief keeps them: the raggedness was a width problem, not a copy one.
     Latest guides and the brand take what is left. */
  .shf-footer__main { grid-template-columns: 1.5fr 0.95fr 1.35fr 1.1fr; }
  .shf-footer__main { grid-template-columns: 1.5fr 0.95fr 1.35fr 1.1fr; }
  .shf-footer__brand { max-width: none; }
  /* Ten boroughs in one column is a tall thin stack beside two short link
     lists. Two columns halves it and squares the row off. */
  /* Three sub-columns, not two: ten short borough names in four rows rather
     than five, which pulls the stacked cell down toward the other three. */
  .shf-footer__areas ul { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--s-3); }
}
/* Five elements at four sizes in one column. The phone drops a step so it
   stops competing with the one in the closing band, and the hours and the
   address line go quiet, which leaves three levels instead of four. */
.shf-footer__meta { color: var(--on-ink-quiet); }
/* The footer ran 1723px on a phone, two and a half times the call to
   action above it, so the last thing a reader met was four stacked columns
   of links. Below the rail each list collapses behind its own title; above
   it they are forced open and the summary is inert, so a desktop reader
   sees exactly what they saw before. Two mechanisms, because browsers
   moved from display-on-children to ::details-content. */
.shf-footer__group summary { cursor: pointer; list-style: none; }
.shf-footer__group summary::-webkit-details-marker { display: none; }
@media (max-width: 1023.98px) {
  .shf-footer__group summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-4);
    min-height: 44px;
    margin-bottom: 0;
  }
  .shf-footer__group summary::after {
    content: "";
    width: 10px;
    height: 10px;
    border-right: 2px solid var(--primary);
    border-bottom: 2px solid var(--primary);
    transform: rotate(45deg);
    transition: transform var(--ease);
  }
  .shf-footer__group[open] summary::after { transform: rotate(-135deg); }
  .shf-footer__group[open] summary { margin-bottom: var(--s-3); }
  .shf-footer__group + .shf-footer__group { border-top: 1px solid var(--line-dark); }
}
@media (min-width: 1024px) {
  .shf-footer__group > *:not(summary) { display: block; }
  .shf-footer__group::details-content { content-visibility: visible; }
  .shf-footer__group summary { pointer-events: none; }
  .shf-footer__group summary::after { content: none; }
}
.shf-footer__title { margin-bottom: var(--s-4); font-size: var(--t--1); font-weight: 700; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--white); }
.shf-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
/* Link rows were 44px of target plus 8px of gap. The target stays; the gap
   goes, because a 44px row is already its own separation. */
.shf-footer__main ul { gap: 0; }
/* Wrapped labels were breaking the rhythm because a two-line link still
   only claimed 44px, so the rows drifted. Let the row grow with the text
   and keep 44px as the floor, and the column stops looking ragged. */
.shf-footer__main li a { min-height: 44px; padding-block: var(--s-2); align-items: center; }
.shf-footer__areas ul { gap: var(--s-2); }

.shf-footer li a { display: inline-flex; align-items: center; min-height: 44px; }
/* Same size, weight and glyph as the number in the closing band 300px
   above it. It was --t-1 there and --t-0 here, which made one number look
   like two different things on one screen. */
.shf-footer__phone { display: inline-flex; align-items: center; gap: var(--s-2); min-height: 44px; font-size: var(--t-0); font-weight: 700; letter-spacing: var(--track-tight); color: var(--white); text-decoration: none; }
.shf-footer__phone:hover { color: var(--primary); text-decoration: none; }
.shf-footer__legal { padding-block: var(--s-5); border-top: 1px solid var(--line-dark); font-size: var(--t--1); color: var(--on-ink-quiet); }
.shf-footer__legal-inner { display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5); align-items: center; justify-content: space-between; }
.shf-footer__legal ul { display: flex; flex-wrap: wrap; gap: var(--s-4); }
/* Regulatory, so it stays legible: 14px is the floor and this sits on it,
   with the quiet ink tone and a long measure so it reads as the fine print
   it is rather than as a second copyright line. */
.shf-footer__disclaimer {
  margin-top: var(--s-4);
  max-width: 80ch;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--on-ink-quiet);
}

/* ==========================================================================
   20. MOTION + PRINT
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .btn:active { transform: none; }
}

@media print {
  .shf-header, .shf-footer, .callbar, .callband, .shf-cta-band,
  .shf-inline-cta, .shf-sidebar, .shf-post-nav, .shf-comments,
  .form, .pc-form, .btn { display: none !important; }
  body { color: #000; padding-bottom: 0; font-size: 12pt; }
  .entry-content a::after { content: " (" attr(href) ")"; font-size: 9pt; word-break: break-all; }
}

/* ==========================================================================
   TRUST STRIP
   Utility band directly under the hero. The hero is --ink, so this band is
   also --ink to keep teal icons at 7.7:1 (teal on --surface is ~2:1 and
   fails 1.4.11). Separation from the hero is carried by a --primary top
   rule plus the tighter --band--tight rhythm, not by a second dark tone.
   No orange: --cta is reserved for call-to-action buttons.
   ========================================================================== */
/* White, per the design. The offer band above is --surface and Benefits
   below is --surface, so this strip is the pale gap between them. */
.trustbar {
  background: var(--white);
  color: var(--body);
  border-block: 1px solid var(--line);
}
/* One thin ribbon. Icon left of a label on the same line, every item on a
   single line at every width above the phone rail. The previous grid put the
   icon above the label and let three of the five wrap onto two lines. */
.trustbar__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}
.trustbar__item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-width: 0;
}
.trustbar__item svg { flex: 0 0 auto; color: var(--hover); }
.trustbar__label {
  font-size: var(--t-0);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: var(--track-tight);
  color: var(--ink);
  white-space: nowrap;
}
@media (min-width: 768px) {
  .trustbar__list {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--s-4) var(--s-6);
  }
}
@media (min-width: 1024px) {
  .trustbar__list { flex-wrap: nowrap; gap: var(--s-7); }
}

/* ==========================================================================
   REPEAT POSTCODE STRIPS
   Compact repeats of step one, placed after blocks that have just done
   persuasion work. Field and button styling is inherited wholesale from
   .pc-form so they cannot drift from the hero. --cta is on the submit
   button only; the strip itself never uses it.
   ========================================================================== */
.pcblock--ink   { background: var(--ink);     color: var(--on-ink); }
.pcblock--brand { background: var(--primary); color: var(--ink); }

.pcblock__inner { display: grid; gap: var(--s-4); }
@media (min-width: 900px) {
  .pcblock__inner {
    grid-template-columns: 1fr minmax(21rem, auto);
    align-items: center;
    gap: var(--s-6);
  }
}

/* The closing block is the only one that passes a heading, and auto
   placement was dealing its seven children alternately left and right:
   the two headings finished in different columns with a gap between them,
   and "complete the form below" sat across a column gap from the form,
   which made the sentence untrue as laid out. Explicit columns instead --
   words left, the form and the line that introduces it right. The two
   repeat strips pass no heading and keep auto placement, so they do not
   move. */
@media (min-width: 900px) {
  /* ONE column, not two. Two columns put the two headings 460px apart in
     separate columns, both introducing the same single action, and left
     each column ending in a different amount of empty navy. There is one
     action here, so there is one column: heading, sub-heading and line as
     a block, the form and its own intro beneath, then the phone and email
     behind a rule as the alternative for someone who will not type. */
  .pcblock__inner--full {
    grid-template-columns: minmax(0, 66ch);
    justify-content: start;
    align-items: start;
    gap: var(--s-3);
  }
  .pcblock__inner--full > * { grid-column: 1; }
  .pcblock__inner--full .pcblock__headline { margin-top: var(--s-5); }
  .pcblock__inner--full .pcblock__contact {
    margin-top: var(--s-5);
    padding-top: var(--s-5);
    border-top: 1px solid var(--line-dark);
  }
}
.pcblock__headline {
  margin: 0;
  max-width: 30ch;
  font-size: var(--t-1);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: var(--track-tight);
}
.pcblock--ink   .pcblock__headline { color: var(--white); }
.pcblock--brand .pcblock__headline { color: var(--ink); }
.pcblock__form,
.pcblock__resume { margin: 0; }

/* Closing block only. The heading pair sits above the headline that labels
   the form, so the hierarchy reads section -> offer -> form. Every one of
   these renders only when its arg is passed, which the two repeat strips
   above do not do. */
.pcblock__heading { margin: 0 0 var(--s-2); color: var(--white); }
.pcblock__sub {
  margin: 0 0 var(--s-2);
  font-size: var(--t-1);
  letter-spacing: var(--track-h3);
  color: var(--white);
}
.pcblock__line { margin: 0 0 var(--s-5); font-size: var(--t-0); color: var(--on-ink); }
.pcblock__intro { margin: 0 0 var(--s-4); font-size: var(--t-0); line-height: 1.55; color: var(--on-ink); }
.pcblock__contact {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3) var(--s-6);
  margin: var(--s-5) 0 0;
}
.pcblock__contact a {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  font-size: var(--t-0);
  font-weight: 700;
  color: var(--white);
}
/* No underline, matching the footer number. One phone number should not look
   like two different things 300px apart. */
.pcblock__contact a { text-decoration: none; }
.pcblock__contact a:hover, .pcblock__contact a:focus-visible { color: var(--primary); text-decoration: underline; }

/* Repeat strips only: stack field over button on small phones so the field is
   full-width and the button is a full-width target. The hero's own .pc-form
   is deliberately left alone. */
@media (max-width: 559.98px) {
  .pcblock__form { display: grid; grid-template-columns: 1fr; gap: var(--s-3); }
  .pcblock__form .pc-form__field { flex: none; }
  .pcblock__form .btn { width: 100%; }
}

/* Certainty Calendar medallions.
   The 32px .steps__dot and the 2px spine already existed; this only puts an
   identifying mark inside each node. The original semantics are preserved on
   purpose: "today" stays filled, upcoming steps stay unfilled rings, so the
   icon says WHAT the step is while the fill still says WHETHER it has
   happened. Medallion size is unchanged, so the column does not widen and the
   hero does not grow. */
.steps__dot svg { display: block; }
.steps li:not(.is-now) .steps__dot { color: var(--hover); }

/* ==========================================================================
   PROCESS FLOW  (#the-wait, part 1)
   Same four rows as the Certainty Calendar, at full width. Connector reuses
   the .steps li::before technique rather than a new one: a single absolutely
   positioned rule per step, suppressed on :last-child so there is no orphan
   segment. No ::after anywhere, so nothing hangs before step 1 either.
   ========================================================================== */
.flow {
  list-style: none;
  margin: 0 0 var(--s-8);
  padding: 0;
  display: grid;
  gap: var(--s-6);
}
.flow__step {
  position: relative;
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: var(--s-4);
  align-items: start;
}
.flow__medallion {
  position: relative;
  z-index: 1;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--primary);
  display: grid;
  place-items: center;
}
.flow__medallion svg { display: block; }
.flow__body { display: grid; gap: 2px; }
.flow__date {
  font-size: var(--t-1);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  color: var(--ink);
}
.flow__what {
  font-size: var(--t-0);
  font-weight: 700;
  line-height: 1.3;
  color: var(--ink);
}
.flow__why { font-size: var(--t--1); line-height: 1.45; color: var(--ink); }

/* Connector — vertical spine while the flow is one column */
.flow__step::before {
  content: "";
  position: absolute;
  left: 27px;
  top: 56px;
  bottom: calc(var(--s-6) * -1);
  width: 2px;
  background: rgba(11, 36, 54, .28);
}
.flow__step:last-child::before { display: none; }

/* Desktop — four across, connector turns horizontal */
@media (min-width: 1024px) {
  .flow { grid-template-columns: repeat(4, 1fr); }
  .flow__step { grid-template-columns: 1fr; justify-items: start; gap: var(--s-3); }
  .flow__step::before {
    left: 56px;
    top: 27px;
    right: calc(var(--s-6) * -1);
    bottom: auto;
    width: auto;
    height: 2px;
  }
}

/* ==========================================================================
   SECTION HEADERS — two treatments, chosen by content, not by position
   --------------------------------------------------------------------------
   .band-head was capped at 40ch, rendering 378px inside a 1200px container:
   31% used, 69% dead, on five consecutive sections. The fix is not to let
   headings run to 1200px — that wrecks the measure. Instead the header uses
   the band width, while the text inside it keeps a readable one.

   The rule is derived from the content: a section that carries introductory
   prose sets it beside the heading; a section whose heading stands alone
   gets a rule that carries the width the words should not.

   :has() is progressive. Where it is unsupported the original single-column
   header renders exactly as before — nothing breaks.
   ========================================================================== */
@media (min-width: 1024px) {

  /* Treatment 1 — heading left, intro prose right. */
  .band-head:has(p) {
    max-width: none;
    display: grid;
    grid-template-columns: minmax(0, 26ch) minmax(0, 46ch);
    column-gap: var(--s-8);
    align-items: start;
  }
  .band-head:has(p) > .label { grid-column: 1; grid-row: 1; }
  .band-head:has(p) > h2     { grid-column: 1; grid-row: 2; margin-bottom: 0; }
  .band-head:has(p) > p      { grid-column: 2; grid-row: 2; margin: 0; align-self: start; }
  /* Every paragraph was pinned to row 2 of column 2, so a header carrying
     two of them stacked one exactly on top of the other and the text was
     unreadable. Only the first belongs in that cell; any that follow go
     down the same column. */
  .band-head:has(p) > p ~ p { grid-row: auto; margin-top: var(--s-4); }

  /* Treatment 2 — heading alone, a hairline carries the remaining width. */
  .band-head:not(:has(p)) {
    max-width: none;
    display: grid;
    grid-template-columns: minmax(0, 34ch) 1fr;
    column-gap: var(--s-8);
    align-items: end;
  }
  .band-head:not(:has(p)) > .label { grid-column: 1; grid-row: 1; }
  .band-head:not(:has(p)) > h2     { grid-column: 1; grid-row: 2; margin-bottom: 0; }
  .band-head:not(:has(p))::after {
    content: "";
    grid-column: 2;
    grid-row: 2;
    height: 2px;
    background: currentColor;
    opacity: .16;
    margin-bottom: .6em;
  }
}

/* Tablet — one treatment, not two. At 753px a two-column header would leave
   26ch and 46ch fighting over the same row; the heading-plus-rule form still
   uses the width honestly and the intro prose simply sits below it. */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .band-head {
    max-width: none;
    display: grid;
    grid-template-columns: minmax(0, 30ch) 1fr;
    column-gap: var(--s-6);
    align-items: end;
  }
  .band-head > .label { grid-column: 1; grid-row: 1; }
  .band-head > h2     { grid-column: 1; grid-row: 2; margin-bottom: 0; }
  .band-head > p      { grid-column: 1 / -1; grid-row: 3; max-width: 52ch; margin-top: var(--s-4); }
  .band-head::after {
    content: "";
    grid-column: 2;
    grid-row: 2;
    height: 2px;
    background: currentColor;
    opacity: .16;
    margin-bottom: .6em;
  }
}

/* ==========================================================================
   COMPACT BANDS  (.band--compact)
   Two sections carry a heading and one row of cards and nothing else. Under
   the standing header treatment the heading was capped at 34ch, which wrapped
   both onto two lines and parked a hairline stub in the empty column beside
   them. Here the heading takes the band width, so each sits on one line, and
   the rule runs the full width underneath, where it reads as a divider rather
   than an offcut. A modifier, so the other three headers are untouched.
   ========================================================================== */

.band--compact .band-head {
  max-width: none;
  display: block;
  margin-bottom: var(--s-6);
}
.band--compact .band-head > h2 { margin-bottom: 0; }
/* Treatment 1 set the intro beside the heading, which is what squeezed the
   comparison band's h2 into 246px and four lines. Stacked, the heading gets
   the band and the intro keeps a reading measure. */
.band--compact .band-head > p { margin: var(--s-4) 0 0; max-width: 62ch; }

/* ==========================================================================
   MOTION  --  the won't-do and three-step cards
   Every rule below sits inside prefers-reduced-motion: no-preference, so under
   reduce there is nothing to switch off: the declarations do not exist. The
   entrance is additionally inside @supports, and nothing outside the keyframes
   ever sets opacity: 0, so without scroll-driven animation the cards simply
   render in their final state. A card can never be left invisible.
   ========================================================================== */

@keyframes shf-card-rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: no-preference) {

  /* The lift survives only where it is honest. The step cards are links now,
     so it is the standard affordance doing the job it advertises. The
     won't-do and Benefits cards are not targets and no longer pretend to
     be: on those the icon alone answers the pointer, and the won't-do cards
     carry no icon, so they answer it not at all. */
  .steps3__item:has(.steps3__link) { transition: transform 200ms ease, box-shadow 200ms ease; }
  .steps3__item:has(.steps3__link):hover,
  .steps3__item:has(.steps3__link):focus-within {
    transform: translateY(-3px);
    box-shadow: var(--shadow-2);
  }
  .steps3__icon { transition: background-color 200ms ease; }
  .steps3__item:hover .steps3__icon,
  .steps3__item:focus-within .steps3__icon { background: rgba(11, 36, 54, .18); }
  .benefits__icon { transition: color 200ms ease; }
  .benefits__item:hover .benefits__icon { color: var(--ink); }

  /* Scroll driven, so it costs no timer and cannot fire early or late. The
     stagger shifts each card range rather than adding a delay: on a desktop
     row all three share one scroll position, where a delay would do nothing. */
  @supports (animation-timeline: view()) {
    /* The won't-do band is deliberately absent. It is the first of four
       card bands and largely above the fold at desktop, so it animated
       least usefully; leaving it still gives the eye one motionless band
       before the three that move. */
    .steps3__item,
    .benefits__item,
    .compare__col,
    .anycond__tile,
    .sit-tick li,
    .anycond__prose p {
      animation: shf-card-rise linear both;
      animation-timeline: view();
      animation-range: entry 10% entry 55%;
    }
    /* 4% a card, not 6%. Five items at 6% would have left the last one
       arriving 24% of an entry behind the first, which reads as a queue
       rather than a sequence. The narrower step also brings the two
       four-card and three-card bands into the same rhythm. */
    .steps3__item:nth-child(2),
    .benefits__item:nth-child(2),
    .compare__col:nth-child(2) { animation-range: entry 14% entry 59%; }
    .steps3__item:nth-child(3),
    .benefits__item:nth-child(3) { animation-range: entry 18% entry 63%; }
    /* Benefits row two restarts the stagger instead of continuing it. Those
       two cards already enter later by sitting lower on the page, and
       compounding a delay on top of that gap is what makes a last card
       feel abandoned. */
    .benefits__item:nth-child(4) { animation-range: entry 10% entry 55%; }
    .benefits__item:nth-child(5) { animation-range: entry 14% entry 59%; }
    .anycond__prose p:nth-child(2) { animation-range: entry 14% entry 59%; }
    .anycond__prose p:nth-child(3) { animation-range: entry 18% entry 63%; }
    /* Eight items in two steps, not eight: sequentially the last would
       trail the first by 28% of an entry and read as a queue.

       n+5 is the column boundary now that the tiles fill column-first: items
       1-4 are the left column and 5-8 the right, so the two steps are the two
       columns. It was a row boundary under the old row-flow list and the
       number did not have to change, but what it means did. */
    .anycond__tile:nth-child(n+5),
    .sit-tick li:nth-child(n+5) { animation-range: entry 14% entry 59%; }
  }
}

/* Explicit, so reduce is unambiguous on inspection rather than inferred from
   the absence of rules above. */
@media (prefers-reduced-motion: reduce) {
  .steps3__item,
  .steps3__icon,
  .steps3__link,
  .benefits__item,
  .benefits__icon,
  .compare__col,
  .anycond__tile,
  .sit-tick li,
  .gm__area,
  .gm__ring,
  .anycond__prose p,
  .chips li {
    animation: none;
    transition: none;
    transform: none;
  }
}

/* ==========================================================================
   AREAS PHOTO  ·  ANY CONDITION  ·  CLOSING CTA
   ========================================================================== */

/* Reserved, empty, and costing nothing. aspect-ratio holds a 3:2 box open so
   the page does not move when a real photograph lands in it. No <img>, so no
   request is spent while it is unfilled. */
.areas__photo {
  aspect-ratio: 3 / 2;
  width: 100%;
  max-width: 40rem;
  margin: 0 0 var(--s-7);
  border: 1px dashed var(--line-dark);
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .04);
}

/* Map left, words right. The map is the strongest visual asset on the page
   and had been floating between two text blocks with no relationship to
   either. Map first in the DOM, so it also leads on a phone. */
.areas__grid { display: grid; gap: var(--s-6); }
@media (min-width: 1024px) {
  .areas__grid {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    gap: var(--s-8);
    align-items: start;
  }
}

.areas__sub {
  margin: 0 0 var(--s-4);
  font-size: var(--t-1);
  letter-spacing: var(--track-h3);
  color: var(--white);
}

/* Reassurance, not a claim. Reading width, no chips and no icons, so it does
   not read as a third restatement of the trust strip chip. */
.anycond__title { margin: 0; }
/* Three paragraphs at 62ch left the right half of the band empty. Balanced
   columns above the rail, the same treatment the agent-case prose uses, so
   the band is filled without the measure getting longer. */
.anycond__prose p {
  max-width: 62ch;
  font-size: var(--t-0);
  line-height: 1.6;
  margin: 0 0 var(--s-4);
  color: var(--ink);
  break-inside: avoid;
}
.anycond__prose p:last-child { margin-bottom: 0; }
/* Prose left, the eight conditions right. The prose stops being two
   balanced columns because it now has something beside it. */
.anycond__grid { display: grid; gap: var(--s-6); }
/* THE EIGHT CONDITIONS, AS TILES. They were ruled rows -- a component the
   situations band shared, left with one user when that band went to a tick
   list, so it read as a leftover and has gone, .sit-list with it.

   Geometry is NOT new: padding var(--s-5), radius var(--r-md), 1px var(--line)
   are the card system .steps3__item / .benefits__item / .compare__col share.
   Fill is var(--surface); at 1.06:1 on white the border draws the tile and the
   fill only stops it reading as an outline. No hover, no icons, no ticks --
   eight nouns, not eight controls, and the tick list stays the page's only
   tick treatment.

   COLUMN-FIRST BY grid-auto-flow with four explicit rows: 1-4 down the left,
   5-8 down the right, the client's order. grid-template-columns with the
   default row flow renders the same columns in the wrong order, which is why
   the old markup was interleaved to compensate. It no longer is. Rows are auto
   and items stretch, so a row's two tiles share a height; row four is tallest
   because the eighth term wraps, and that is the bottom row.

   GAP var(--s-3) against the grid's var(--s-6)/var(--s-8) around the set, so
   eight boxes read as one object. Declared HERE, with the rest of .anycond and
   not at the old .sit-list address 1,700 lines away -- that placement took the
   region lint from 7 components to 8, which is the whole point of the lint. */
.anycond__tiles {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-3);
}
.anycond__tile {
  padding: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  font-size: var(--t-0);
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
}
@media (min-width: 768px) {
  .anycond__tiles {
    grid-template-rows: repeat(4, auto);
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
  }
}

@media (min-width: 1024px) {
  .anycond__grid { grid-template-columns: 1fr 1fr; gap: var(--s-8); align-items: start; }
}

/* ==========================================================================
   GREATER MANCHESTER BOROUGH MAP  (#areas)
   Same drawing language as the icons: stroked outlines at the theme's 1.75
   weight, no fills except the one that carries meaning. Manchester is the
   only shaded borough — it reads as the centre without touching --cta.
   Labels are real <text>, so they are selectable and crawlable.
   ========================================================================== */
.gm__svg { display: block; width: 100%; height: auto; }
/* fill and stroke sit on the GROUP, not on the polygon. They are inherited
   SVG properties, so the polygon still paints the same -- but every
   highlight below can now target the group, which is as deep as :has()
   reaches into SVG in Chrome. A rule ending in ` polygon` is silently
   dropped from the cascade there, even with !important. */
.gm__area {
  fill: rgba(255, 255, 255, .05);
  stroke: rgba(255, 255, 255, .40);
}
.gm__area polygon {
  stroke-width: 2;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
}
.gm__area text {
  fill: var(--white);
  font-family: var(--font);
  font-size: 11px;
  font-weight: 700;
  text-anchor: middle;
  letter-spacing: var(--track-tight);
}
/* Manchester — the centre, stated in colour rather than in orange. */
.gm__area--focus { fill: var(--hover); stroke: var(--primary); }

/* The map used to stand down below 768px, because ten labels could not be
   read in a 343px column. It has the full column now rather than a
   fraction of one, so it stays — with the labels stepped up, since they
   scale with the viewBox and 11px in a 396-unit box renders under 10px on
   a phone. */
@media (max-width: 1023.98px) {
  .gm__area text { font-size: 15px; }
  /* Below the rail the grid is one column, so an uncapped map became the
     widest it ever gets -- 821px at 900px wide -- at the width where it
     matters least. Capped and left-aligned, so the band reads as a column
     of content with a map in it. */
  #areas .gm { max-width: 560px; margin-inline: 0; }
  /* Hover does not exist on a phone, so the chip-to-map linkage is desktop
     only and the chips are the functional half: the list you scan for your
     own borough. display:contents lifts them out of the body column so the
     answer comes before the picture. */
  #areas .areas__body { display: contents; }
  #areas .chip-groups { order: -1; }
}

/* --------------------------------------------------------------------------
   The map answers the pointer
   Nothing in the map is a link, so nothing in it is focusable: making ten
   decorative groups tabbable would add ten keyboard stops that go nowhere,
   and the chip list beside it already carries the same ten names as real
   text. Hover only, and the section does not depend on it -- Manchester is
   filled from first paint and every label is readable untouched.
   -------------------------------------------------------------------------- */
.gm__area:hover { fill: rgba(0, 191, 166, .30); stroke: var(--primary); }
.gm__area--focus:hover { fill: var(--primary); }

/* Map to chip only. The reverse -- hovering a chip to light its borough --
   is not achievable in CSS: Chrome does not apply inherited SVG paint
   through a :has() selector. Proved with an !important probe on one
   element at one instant, with the selector confirmed matching:
   outline applied, opacity applied, fill did not. fill and stroke are
   what a borough highlight needs, so that direction is out rather than
   scripted. Hovering the map itself still works -- that is a plain
   descendant rule with no :has() in it. */
#areas .areas__grid:has(.gm__area[data-borough="bolton"]:hover) .chips li[data-borough="bolton"],
#areas .areas__grid:has(.gm__area[data-borough="bury"]:hover) .chips li[data-borough="bury"],
#areas .areas__grid:has(.gm__area[data-borough="manchester"]:hover) .chips li[data-borough="manchester"],
#areas .areas__grid:has(.gm__area[data-borough="oldham"]:hover) .chips li[data-borough="oldham"],
#areas .areas__grid:has(.gm__area[data-borough="rochdale"]:hover) .chips li[data-borough="rochdale"],
#areas .areas__grid:has(.gm__area[data-borough="salford"]:hover) .chips li[data-borough="salford"],
#areas .areas__grid:has(.gm__area[data-borough="stockport"]:hover) .chips li[data-borough="stockport"],
#areas .areas__grid:has(.gm__area[data-borough="tameside"]:hover) .chips li[data-borough="tameside"],
#areas .areas__grid:has(.gm__area[data-borough="trafford"]:hover) .chips li[data-borough="trafford"],
#areas .areas__grid:has(.gm__area[data-borough="wigan"]:hover) .chips li[data-borough="wigan"] {
  background: rgba(0, 191, 166, .22);
  border-color: var(--primary);
  color: var(--white);
}

@keyframes shf-borough-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (prefers-reduced-motion: no-preference) {
  .gm__area { transition: fill 180ms ease, stroke 180ms ease; }
  .chips li { transition: background-color 180ms ease, border-color 180ms ease, color 180ms ease; }

  /* The boroughs arrive outward from the centre rather than in list order.
     Manchester is excluded entirely: it is filled before anything moves and
     stays that way, so the sequence reads as radiating from it rather than
     as ten shapes appearing in an arbitrary order. Opacity only -- a scale
     on a polygon reads as a wobble at this size. */
  @supports (animation-timeline: view()) {
    /* Three wrapper groups rather than nine boroughs. animation-timeline on
       an SVG <g> is thin ice; three plain grouping elements with
       predictable boxes is less of it than nine, and the rings are what
       the sequence is about anyway. Manchester is outside all three and
       never animates. */
    .gm__ring {
      animation: shf-borough-in linear both;
      animation-timeline: view();
      animation-range: entry 12% entry 50%;
    }
    .gm__ring--2 { animation-range: entry 18% entry 56%; }
    .gm__ring--3 { animation-range: entry 24% entry 62%; }
  }
}

@media (min-width: 1024px) {
  /* The stranded space this used to centre away is gone: the map has a
     column of its own and the words have theirs. */
  #areas .gm { margin: 0; }
  #areas .chips { margin: 0; }
  #areas .fine  { margin-top: var(--s-5); }
}

/* ===========================================================================
   TWO-ROW HEADER
   Top row -- brand and the call button -- fixed ABOVE THE RAIL ONLY. On a
   phone the call bar is already 95px of fixed chrome against a 667px
   viewport; a fixed 72px header on top would make it 167px, or 25%, and
   the static mobile header exists because that number was measured once
   already. The nav row always scrolls.

   The header itself must stop being sticky here, or it would carry the nav
   row down the page with it. Sticky on the top row alone cannot work: a
   sticky child is trapped in its parent's box and unsticks at the header's
   own foot. Hence fixed, plus body padding so the fix costs no layout
   shift, plus scroll-margin so in-page anchors land clear of it.
   =========================================================================== */
@media (min-width: 1024px) {
  .shf-header { position: static; }
  .shf-header__top {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 100;
    background: var(--white);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow-1);
  }
  /* One number, used twice, and it has now drifted four times: 76 against an
     82px row, 82 against 90 when the call button grew, 90 against 65 when that
     button was split into a small CTA and two lines of text, and 65 against 77
     when capping the caption at 10rem wrapped it onto a second line. Two hid
     the row below behind the fixed one, one left a band of empty white under
     it. Not one was noticed by looking at the page; the assertion caught every
     one of them.

     The logo landed in chunk 14 and moved it a fifth time, to 107 -- within a
     pixel of the 106 predicted, the only one of the six anticipated rather
     than discovered. Cropping the mark's transparent margin in chunk 15 moved
     it a sixth time, down to 81: the box is 95x64 now instead of 320x90,
     because the ink fills it rather than floating in the middle of it.

     Seventh move, to 97, and the first one a machine found: the geometry probe
     measured the phone link at 166x24 and giving it back its 44px target made
     the contact column 80px tall. 16px of permanent chrome is the price of a
     touch target that a tablet user can actually hit. 14.5% of a 667px
     viewport, well inside the 20% this header has been held to.

     This is the value to change, and .work/ab.sh compares it against the row
     as actually measured. */
  :root { --shf-fixed-row: 97px; }
  body { padding-top: var(--shf-fixed-row); }
  /* Named, not [id]. The blunt version reached every element with an id
     on every template. These are the only in-page anchors the site has. */
  #offer,
  #shf-content,
  #shf-menu,
  #shf-top,
  :target { scroll-margin-top: calc(var(--shf-fixed-row) + 10px); }
  .shf-skip-link { z-index: 110; }
}

/* ===========================================================================
   SOCIAL
   Its own component, not part of .shf-footer -- it was sitting inside that
   region and splitting it. Renders nothing until a Customizer URL exists.
   =========================================================================== */
/* Social. Nothing renders until a URL exists, so this styles a row that is
   absent today. */
.shf-social { display: flex; gap: var(--s-3); list-style: none; margin: var(--s-5) 0 0; padding: 0; }
.shf-social__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--line-dark);
  border-radius: var(--r-md);
  color: var(--on-ink);
}
@media (prefers-reduced-motion: no-preference) {
  .shf-social__link { transition: color var(--ease), border-color var(--ease); }
}
.shf-social__link:hover, .shf-social__link:focus-visible { color: var(--primary); border-color: var(--primary); }

/* ===========================================================================
   OLD-SITE STRIPS
   Two lines of type carried over from sellhousefaster.co.uk. The source set
   each as a short phrase with an emphasised opening -- "There is NO: ... FEE",
   "We buy: ... Houses" -- above a quieter line. No cards, no entrance, no
   hover: the brief rules all three out, and the point of the strip is that it
   is one plain sentence rather than a grid pretending to be four claims.

   Three classes shared by both strips rather than one set each.
   =========================================================================== */
.shf-strip {
  margin: 0;
  font-size: var(--t-1);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: var(--track-h3);
  color: var(--ink);
}
.shf-strip__key { color: var(--hover); }
.shf-strip__foot {
  margin: var(--s-2) 0 0;
  font-size: var(--t--1);
  color: var(--body);
}

/* Modifier, after the base rules, scoped to the one block that still uses the
   strip -- the won't-do band was its other user and has gone.

   The pair sat flush against the grid above them: measured 0px between the
   grid's foot and the strip, so two lines of the client's own closing copy
   read as a stray fourth paragraph. 48px puts them where a closing statement
   goes rather than where the next paragraph would.

   And the caption comes up one step, from 13.6 to 17. At the floor it was
   quieter than the body copy it follows, which made a line the client wrote
   as a call to action read as a footnote to the line above it. */
.anycond .shf-strip { margin-top: var(--s-7); }
.anycond .shf-strip__foot { font-size: var(--t-0); }
/* Full width, below both columns. Both <p> already sat outside .anycond__grid
   -- structurally they were never in the left column -- but every <p> on this
   site takes `max-width: var(--measure)` from the element rule, and measured
   at 1280 that left the strip at 732px and the closing line at 586px against a
   1136px rail. The dead space to their right was the measure cap, not the
   markup, so the fix is here and the markup did not move. */
.anycond .shf-strip,
.anycond .shf-strip__foot { max-width: none; }


/* The closing block's number and address each get the old site's caption
   under them. --on-ink-quiet is 7.4:1 on the ink ground this block always
   uses; shf_postcode_block() only ever renders contact on the ink variant. */
.shf-contactline {
  display: inline-flex;
  flex-direction: column;
  gap: 2px;
}
.shf-contactline__cap {
  font-size: var(--t--1);
  font-weight: 400;
  color: var(--on-ink-quiet);
}

/* ==========================================================================
   REASONS-FOR-SALE GRID
   Twelve horizontal cards: icon block left, heading and one clamped line
   right. Replaces the portrait grid, which at 1024-1440 produced 181px-wide
   cards 405px tall inside a 1200px wrap.
   ========================================================================== */

/* --- 1. Escape the prose measure, for the grid only -----------------------
   .situation-body caps at 38.5rem so paragraphs keep a readable line length,
   and that cap must keep applying to prose. The grid is not prose and was
   inheriting it: at 1440 the grid sat in 616 of a 1200px wrap with 584px
   unused either side.

   The cap comes off the container and goes back on to everything inside it
   EXCEPT the grid, so prose keeps both its measure and its left edge and only
   the grid spans the wrap. Scoped with :has() so the eleven other pages using
   situation-body are untouched -- none of them contains a .reasons.

   Without :has() support the whole thing simply does not match and the grid
   stays capped at 616px, which is where it is today. Degraded, not broken. */
/* The rest of the preamble, same scope. At 1280 the band alone contributed
   90px of top padding and the grid another 32px of margin above a heading
   that already carries its own margin below it. The hero's own two overrides,
   its padding and its H1 size, live with the rest of .shf-page-hero so that
   component stays in one region. None of this leaves this page. */body:has(.reasons) .band:has(.reasons) { padding-top: var(--s-5); }
body:has(.reasons) .reasons { margin-top: var(--s-4); }

.situation-body:has(> .reasons) { max-width: none; }
.situation-body:has(> .reasons) > :not(.reasons) { max-width: 38.5rem; }
/* And the grid's own cap. .reasons is a <ul>, so `.entry-content ul` hands it
   the prose measure on its own account at 0,0,1,1 -- lifting the container
   alone left the grid pinned at 732px inside a 1200px wrap. Scoped the same
   way and specific enough (0,0,3,0) to win without !important. */
.situation-body:has(> .reasons) > .reasons { max-width: none; }

/* Without :has() -- Firefox before 121, Safari before 15.4 -- neither rule
   above matches and the grid is left at the container's own 616px. Measured,
   not assumed: disabling both rules at runtime gives exactly 616.

   -10rem, not the full escape. The escape differs by width: 164px each side at
   the 1024 rail, 260px at 1440. One fixed value has to be safe at the narrow
   end or it overflows, so this takes the smaller. Measured at 1440 it gives
   936px of grid against 1136 with :has(), and does not overflow. Wrong-but-
   safe beats right-and-clipped, and the browsers needing it are a shrinking
   tail. */
@supports not selector(:has(*)) {
  @media (min-width: 1024px) {
    .situation-body .reasons {
      max-width: none;
      margin-inline: -10rem;
    }
  }
}

/* --- 2. The grid --------------------------------------------------------- */
.reasons {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-4);
  margin: var(--s-6) 0 0;
  padding: 0;
  list-style: none;
}

/* --- 3. The card --------------------------------------------------------- */
/* Two columns that both run the full card height. Before this the icon was a
   52px tile pinned to the top of a 155px card, so the copy ran on past the
   bottom of it and the card read as a line of text with a small mark stuck to
   the left. A teal column the height of the card is a different object. */
.reasons__card {
  position: relative;
  display: flex;
  align-items: flex-start;
  margin: 0;
  padding: 0;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}
/* The theme gives list items in .entry-content a bottom margin, at 0,0,1,2.
   A margin:0 on .reasons__card alone is 0,0,1,0 and was losing, so every card
   carried 8px below it on top of the grid gap -- 40px of unaccounted height
   down a six-row grid, which is what kept the second row off the fold at 1280.
   Two classes wins it without !important. */
.reasons > .reasons__card { margin: 0; }

/* Teal ground with the glyph reversed out. A tinted square read as a swatch
   behind a small mark; a solid one reads as an object, which is what carries
   a horizontal card. --cta stays reserved for the enquiry buttons. */
/* Square below the 900 rail, full-height column above it. Stretched at 360
   this was 80 wide by 185 tall with a 40px glyph adrift in the middle of it:
   a saturated stripe down the side of the card rather than a mark on it.
   A full-height column only works once the card is wide enough to be read
   across rather than down. */
.reasons__icon {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 5rem;
  height: 5rem;
  background: var(--primary);
  color: var(--white);
}
/* The shortcode writes width and height attributes on the svg; CSS outranks a
   presentation attribute, so the glyph is sized here rather than by editing
   twelve size="" values in the page content. */
.reasons__icon svg { width: 2.5rem; height: 2.5rem; }

/* min-width:0 or the flex item refuses to shrink below its longest word and
   the clamp never gets the chance to bite. The card's padding lives here now,
   because the icon column has to reach the card's edges.

   Column flex, centred, so the heading and its sentence read as one block set
   against the icon instead of being pushed apart to the card's top and bottom
   edges once the row stretches the card past its own content height. */
.reasons__body {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--s-4);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* min-height:2.5em used to sit here, reserving a second line so a one-line
   and a two-line heading could not give one row two card heights. Measured,
   it was reserving that line on all twelve at once: every heading fits a
   single line from the 900 rail up, so every card carried 29px of empty box
   and the gap from heading to sentence measured 39px where the margin asks
   for 8. Equal row heights are the grid's job anyway -- grid items stretch to
   the tallest in their row without being told -- so the reservation bought
   nothing and cost a hole in the middle of every card. */
.reasons__title {
  margin: 0 0 var(--s-2);
  font-size: var(--t-1);
  line-height: 1.25;
}
.reasons__title a { color: var(--ink); text-decoration: none; }

/* Two lines on every card, whatever the source. Ten of the twelve carry the
   old site's /services/ tile at roughly 70 characters; the two with no tile
   run 146 and 163, and that 109px spread is what made every row bottom out
   ragged. The clamp makes card height a design decision rather than a
   copy-length accident, and stops truncating the moment two short lines
   arrive for the other two. */
.reasons__card p {
  margin: 0;
  font-size: var(--t--1);
  color: var(--body);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

/* Stretched link, not an <a> wrapped round the card, so the accessible name
   stays the situation instead of the situation plus its sentence. Same
   pattern as .steps3__link on the homepage. */
.reasons__title a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--r-lg);
}
/* The ring belongs on the card, not on the heading text inside it. */
.reasons__card:has(.reasons__title a:focus-visible) {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
}
@supports not selector(:has(*)) {
  .reasons__title a:focus-visible {
    outline: 3px solid var(--ink);
    outline-offset: 4px;
    border-radius: var(--r-sm);
  }
}

/* --- 4. The closing note -------------------------------------------------
   The three paragraphs under "Everything else" ran at the 38.5rem prose
   measure inside a 1136px band, leaving the right 520px of it empty on every
   desktop screen while every other band on the site fills its width.

   Two columns rather than one long line. The measure exists because 616px is
   a comfortable distance for an eye to travel; widening it to 1136 would fix
   the layout by breaking the typography. Below the 900 rail it stays one
   column, where the measure was never the problem.

   The escape has to come after the :not(.reasons) cap higher up: both
   selectors are 0,0,3,0, so the later one wins and order is load-bearing. */
.situation-body:has(> .reasons) > .reasons__outro { max-width: none; }

@media (min-width: 900px) {
  .reasons__outro { columns: 2; column-gap: var(--s-6); }
  /* A paragraph broken across the gutter reads as two paragraphs. */
  .reasons__outro > p { break-inside: avoid; }
  .reasons__outro > p:first-child { margin-top: 0; }
}

/* --- 5. Reserved, empty, costing nothing --------------------------------
   The two heaviest trust elements on the reference page are a reviews strip
   and an accreditation row, and the client has neither. Same treatment as
   .areas__photo: the rules hold the space so the page does not jump the day
   real ones land, and the flags in template-situation.php are false, so
   nothing renders and no request is spent meanwhile. A dashed empty frame on
   a staging URL announces a hole rather than reserving one. */
.reasons-reviews {
  min-height: 8.5rem;
  margin: var(--s-6) 0 0;
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
}
.reasons-accred {
  min-height: 4.5rem;
  margin: var(--s-5) 0 0;
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
}

/* --- 6. Two columns from the 900 rail ------------------------------------
   Horizontal cards are wider and shorter than the portrait ones were, so the
   column count drops. Three WAS measured at 1440 rather than assumed, and it
   is not obviously worse: 355px cards, grid 721px tall against 912, and a
   height spread of 0 because at that width every heading wraps to two lines
   and they all equalise.

   It stops at two anyway, on the text column. Three leaves 237px beside the
   icon; two leaves 417px. At 237px "Selling a probate or inherited property"
   takes two lines before the copy starts, and the card becomes a narrow
   column of text with a picture stuck to it. The 191px saved is not worth
   that, and the page is 912px of grid either way -- roughly one screen. */
@media (min-width: 900px) {
  .reasons { grid-template-columns: repeat(2, 1fr); gap: var(--s-5); }
  .reasons__card { align-items: stretch; }
  .reasons__body { padding: var(--s-5); }
  .reasons__icon { width: 7.5rem; height: auto; }
  .reasons__icon svg { width: 4rem; height: 4rem; }
}

/* Two lines from 1024 up, three below it.
   Three is the default because a 304px card at 360 holds about 55 characters
   on two lines while the copy runs 61-79, and a two-line clamp cut ten of the
   twelve mid-sentence.
   The flip is at 1024 rather than 900 because 900-1023 is two columns of only
   265px, where seven cards truncated. At 1024 the text column reaches 327px
   and two lines holds every one. */
@media (min-width: 1024px) {
  .reasons__card p { -webkit-line-clamp: 2; line-clamp: 2; }
}

@media (prefers-reduced-motion: no-preference) {
  .reasons__card { transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease; }
  .reasons__card:hover,
  .reasons__card:focus-within {
    transform: translateY(-2px);
    box-shadow: var(--shadow-2);
    border-color: var(--primary-28);
  }
  .reasons__icon { transition: background-color 200ms ease; }
  .reasons__card:hover .reasons__icon,
  .reasons__card:focus-within .reasons__icon { background: var(--hover); }

  /* Entrance is opacity only, deliberately. An animation with fill both ends
     on transform:none, which outranks the hover transition for the rest of
     the page's life -- animate the card's transform on entry and the hover
     lift silently stops working after the card has arrived. The boroughs
     block above is opacity-only for its own reasons; this one is for that.

     Staggered by column, not by card: the right-hand card of each pair
     starts a little later, so every row behaves identically and the twelfth
     card arrives exactly as promptly as the second. Sequencing twelve cards
     would leave the last row visibly trailing. */
  @supports (animation-timeline: view()) {
    .reasons__card {
      animation: shf-reason-in linear both;
      animation-timeline: view();
      animation-range: entry 4% entry 34%;
    }
    .reasons__card:nth-child(2n) { animation-range: entry 9% entry 39%; }
  }
}

@keyframes shf-reason-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
