/* ==========================================================================
   Climate Shield London: the design system.

   This file replaces main.css and extra.css. It exists because the first build
   accumulated nine blues, an amber, a yellow, a green and a red with no rule
   about which one meant what, and the result read as generated rather than
   designed. Everything below is locked.

   THE FOUR LOCKS
   1. THEME.    Graphite ink and warm paper, used as a fixed rhythm: dark hero,
                light reading sections, dark call to action, dark footer. A
                section never invents its own mode.
   2. COLOUR.   Two accents and each has one job.

                ICE (#63BCE6). Action AND brand. A light sky blue used as a
                FILL WITH DARK TEXT, which is the whole trick: at 8.2:1 against
                the ink it is more readable than any dark blue button with white
                text, it separates cleanly from white, and nobody else in this
                trade does it. Deeper members of the same ramp carry links
                (#166B99 on paper) and highlights (#8FD1F0 on ink).

                WARM (#E1562B). A TINT ONLY, never a fill under text. It marks
                heating and hot water on the heat pump, solar and battery
                cards. It is not a button and must never become one.

                What is gone: the orange buttons and the sea green, both of
                which David rejected. What stays gone: the nine competing blues
                of the first build. One ramp, stated once.

                WhatsApp green belongs to WhatsApp and appears nowhere else.
   3. SHAPE.    Pill for anything interactive. 20px for panels and cards. 12px
                for inputs. No other radius exists.
   4. TYPE.     Bricolage Grotesque for display, Lexend for everything else.
                One scale, defined once, used everywhere.

   HOUSE RULES STILL IN FORCE
   No eyebrow labels above headings. No em-dashes. No decorative dots. No
   scroll cues. No stock photography. The static, no-motion view must look
   finished, because that is the view David sees.
   ========================================================================== */

@font-face{font-family:"Bricolage Grotesque";src:url(../fonts/bricolage-var.woff2)format("woff2");font-weight:600 800;font-display:swap}
@font-face{font-family:Lexend;src:url(../fonts/lexend-400.woff2)format("woff2");font-weight:400;font-display:swap}
@font-face{font-family:Lexend;src:url(../fonts/lexend-500.woff2)format("woff2");font-weight:500;font-display:swap}
@font-face{font-family:Lexend;src:url(../fonts/lexend-600.woff2)format("woff2");font-weight:600;font-display:swap}
@font-face{font-family:Lexend;src:url(../fonts/lexend-700.woff2)format("woff2");font-weight:700;font-display:swap}

/* [hidden] has to win everywhere. The browser's own rule is in the user
   agent sheet and loses to ANY author rule that sets display, so every
   component with a display value silently ignored the attribute: the Back
   button on the first calculator question, the before-grant line on a result
   with no grant, and the "show the other installations" button on a desktop,
   where it sat visible and did nothing. Found 11 September 2026. */
[hidden]{display:none!important}

:root{
  color-scheme:only light;

  /* ---- ink. Slate with a blue cast now, on David's call: the green tint in
         the dark blocks read as green and he wanted it gone. Still not navy,
         which is the colour every other installer uses, and still not black.
         Each step is a real step: these are the five dark surfaces, and a
         section picking one of them is choosing a depth. ---- */
  --ink-950:#0A1119;
  --ink-900:#0F1A24;
  --ink-850:#142333;
  --ink-800:#1A2D40;
  --ink-700:#243D55;
  --ink-600:#33536F;

  /* ---- paper. Warm. A cool grey page is what made the old build feel
         machine made, and it costs nothing to warm it. ---- */
  --paper:#FFFFFF;
  --paper-50:#F7F9FB;
  --paper-100:#EDF2F6;
  --paper-200:#DDE6ED;

  /* ---- ice. The brand and the action. Light fills carry dark text. ---- */
  --ice-900:#0B3A52;
  --ice-800:#11536F;
  --ice-700:#166B99;
  --ice-600:#1E86BC;
  --ice-500:#3AA3D8;
  --ice-400:#63BCE6;
  --ice-300:#8FD1F0;
  --ice-200:#B3DFF5;
  --ice-100:#D5EDFA;
  --ice-50:#EFF8FD;

  /* ---- warm. A tint, never a fill under text. Heating and hot water. ---- */
  --warm:#E1562B;
  --warm-300:#F7A484;
  --warm-100:#FBDDD1;
  --warm-50:#FDF1EB;
  --warm-ink:#8F2F13;

  /* ---- status. Used in verdicts and tables, not as decoration. ---- */
  /* Positive verdicts were green. David: no green anywhere except WhatsApp.
     Every place this is used pairs it with a word or a tick, so the colour is
     not carrying the meaning on its own and a blue reads as "yes" perfectly
     well. --stop stays red, because red is the one signal colour that has no
     substitute. */
  --ok:#166B99;
  --ok-50:#E7F1F8;
  --stop:#C93B3B;
  --hold:#A9701A;

  /* ---- text ---- */
  --ink:#111A22;
  --ink-2:#4B5C68;
  --ink-3:#65757F;
  --on-dark:#E7EEF4;
  --on-dark-2:#AFC0CD;
  --on-dark-3:#7A8D9C;

  /* ---- lines ---- */
  --line:#E2E7EB;
  --line-2:#C7D2DA;
  --line-dark:rgba(216,232,244,.14);
  --line-dark-2:rgba(216,232,244,.26);

  /* ---- type ---- */
  --display:"Bricolage Grotesque",Lexend,system-ui,sans-serif;
  --font:Lexend,ui-sans-serif,system-ui,"Segoe UI",Roboto,Arial,sans-serif;

  /* ---- shape. Three values. There is no fourth. ---- */
  --r-pill:999px;
  --r-panel:20px;
  --r-input:12px;

  /* ---- elevation. Tinted to the ground, never black. ---- */
  --sh-sm:0 6px 18px -10px rgba(17,26,34,.20);
  --sh:0 20px 50px -22px rgba(17,26,34,.28);
  --sh-lg:0 34px 80px -30px rgba(17,26,34,.38);
  --sh-accent:0 18px 42px -18px rgba(30,134,188,.45);

  /* ---- rhythm ---- */
  --pad:clamp(18px,4.5vw,52px);
  --gap:clamp(24px,3.4vw,48px);
  /* 72, not 116. See SECTION RHYTHM just after .band. */
  --band:clamp(44px,5vw,72px);
  --measure:68ch;
  --nav-h:68px;
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:calc(var(--nav-h) + 28px)}
#survey{scroll-margin-top:calc(var(--nav-h) + 22px)}
body{margin:0;background:var(--paper);color:var(--ink);
  font:400 clamp(15.5px,.42vw + 14.4px,17px)/1.62 var(--font);
  -webkit-font-smoothing:antialiased;overflow-x:hidden}
img{max-width:100%;height:auto;display:block}
a{color:inherit}
h1,h2,h3,h4{margin:0;font-family:var(--display);font-weight:800;
  letter-spacing:-.03em;line-height:1.06;text-wrap:balance}
p{margin:0;text-wrap:pretty}
ul,ol{margin:0;padding:0}
table{border-collapse:collapse}
.num,.dtable td,.dtable th,.stat-n,.job dd{font-variant-numeric:tabular-nums lining-nums}

:where(a,button,input,select,summary,[tabindex]):focus-visible{
  outline:3px solid var(--ice-500);outline-offset:3px;border-radius:8px}
.skip{position:absolute;left:-9999px}
.skip:focus{left:12px;top:12px;z-index:200;background:var(--ink-900);color:#fff;
  padding:12px 18px;border-radius:var(--r-pill)}

.wrap{width:min(1240px,100% - var(--pad)*2);margin-inline:auto}
.band{padding-block:var(--band)}
/* SECTION RHYTHM, 12 September 2026. David: far too much distance between
   sections. Measured on the heat pump page at 1366px it was 145 to 223 pixels
   of nothing between one section's last line and the next one's heading,
   because every band carried 116px of padding at each end and two white
   bands in a row add theirs together.

   Two changes. The band padding comes down to 72px at most. And when a
   section follows another on the same ground, the second one adds only a
   fraction of its top padding, because nothing needs separating: there is no
   change of colour for the space to mark, so the space alone was doing the
   job twice. Where the ground does change, white to the tinted grey or back,
   both keep their full padding, because that edge is a real edge.

   Scoped to main's own children, so the sections folded inside the long guide
   keep their own spacing. */
main > :is(.band:not(.band--tint),.marks--accs,.lp-offer-band) + :is(.band:not(.band--tint),.marks--accs,.lp-offer-band),
main > .band--tint + .band--tint{padding-top:calc(var(--band) * .35)}
/* A change of ground, white to the tinted grey or back, keeps an edge on
   both sides, but not a full band each: the grey is #F7F9FB, close enough to
   white that two full paddings still read as a hole with a faint line in it.
   Seven tenths on each side keeps the heading off the colour edge. */
main > .band:not(.band--tint):has(+ .band--tint),
main > .band--tint:has(+ .band:not(.band--tint)){padding-bottom:calc(var(--band) * .7)}
main > .band:not(.band--tint) + .band--tint,
main > .band--tint + .band:not(.band--tint){padding-top:calc(var(--band) * .7)}
/* Two light grounds and two dark ones. There was a third light one, a visible
   ice blue, and David killed it: a whole section washed in a pale blue reads as
   a coloured box rather than as a page, and it fought the blue that the buttons
   and links are already carrying. Blue on this site is for things you press and
   things you read, not for the paper behind them. */
.band--tint{background:var(--paper-50)}
.band--edge{background:var(--paper-50);border-block:1px solid var(--line)}
/* .band--dark is gone, 11 September 2026. David: no full-width dark blue
   sections anywhere, only cards and panels with their own edges may stay
   dark. The three home sections that used it (the map, the integrated
   systems and the recent installs) are white bands now, and the dark cards
   inside them keep their own colours. */

h1{font-size:clamp(36px,5.4vw,68px);line-height:1.02}
/* THREE HEADING SIZES AND A REASON FOR EACH.

   Before this the site had a section heading at 44, and then panel headings at
   21, 22 and 23, and card headings at 17.5, 19, 20 and 22, all arbitrary. The
   rule now is about what the heading opens, not about how it looks:

     --h-section  opens a band. Every section on every page is this size.
     --h-panel    opens a card, a form or a tool inside a band. Smaller because
                  the column it sits in is narrower, not because it matters less.
     --h-card     opens one item in a grid of items.

   An h2 that is not a section heading takes --h-panel explicitly. There is no
   fourth size, and a new one needs a reason written next to it. */
:root{
  --h-section:clamp(27px,3.1vw,44px);
  --h-panel:22px;
  --h-card:clamp(18px,1.2vw + 12px,21px);
}
h2{font-size:var(--h-section)}
h3{font-size:var(--h-card);font-weight:700;letter-spacing:-.02em}
.lede{color:var(--ink-2);max-width:var(--measure);font-size:clamp(16px,.4vw + 15px,18px)}
.h-block{display:grid;gap:14px;margin-bottom:var(--gap);max-width:var(--measure)}
.h-block--mid{margin-inline:auto;text-align:center}
.h-block--mid .lede{margin-inline:auto}

/* The accent phrase in a headline. A soft underline, not a highlighter. */
/* The underline is a real text-decoration, not a bar drawn under the box.

   It used to be an absolutely positioned ::after, and that only works on a span
   that never wraps, which is why the rule also carried `white-space: nowrap`.
   On a short highlight that is invisible. On a thirty character one it is a
   single unbreakable token in the middle of the headline: `text-wrap: balance`
   cannot do anything with it, so the H1 broke as "Air conditioning for the" /
   "London heat" / "your house was never built for" and the line lengths were
   all over the place, which is what David saw.

   A text-decoration underline draws on every line the span occupies, so the
   span can wrap and the browser can balance the whole headline. */
.hl{color:var(--ice-600);
  text-decoration:underline;text-decoration-thickness:.1em;
  text-underline-offset:.14em;text-decoration-color:color-mix(in srgb,var(--ice-400) 55%,transparent)}
.hero .hl{color:var(--ice-300);
  text-decoration-color:color-mix(in srgb,var(--ice-400) 62%,transparent)}

/* ==========================================================================
   THE CAPSULE NAVIGATION
   A floating pill over the hero, the shape David asked for. It is one line at
   every desktop width, 68px tall, and it becomes a solid bar the moment the
   page scrolls so the links stay readable over light content.
   ========================================================================== */
.navwrap{position:fixed;top:14px;left:0;right:0;z-index:100;pointer-events:none}
.navwrap > .wrap{pointer-events:auto;width:min(1340px,100% - var(--pad)*2)}
.nav{display:flex;align-items:center;gap:8px;min-height:var(--nav-h);
  padding:8px 8px 8px 20px;border-radius:var(--r-pill);
  background:rgba(15,26,36,.62);border:1px solid rgba(216,232,244,.16);
  backdrop-filter:blur(18px) saturate(150%);
  -webkit-backdrop-filter:blur(18px) saturate(150%);
  box-shadow:0 18px 44px -22px rgba(0,0,0,.7);
  transition:background .25s ease,border-color .25s ease,box-shadow .25s ease,
    opacity .28s ease}

/* Scrolled state. Solid ink so it never fights the content behind it. */
.is-stuck .nav{background:rgba(15,26,36,.94);border-color:rgba(216,232,244,.2);
  box-shadow:0 16px 40px -26px rgba(0,0,0,.85)}

/* Receding state, added 2026-09-05. See the note in main.js: this is set while
   the page is being scrolled DOWN and cleared the moment it goes back up.

   0.55 rather than something braver. The bar has to stay legible enough that a
   visitor scrolling past can still see where the phone number is; this is a
   step back, not a disappearance. Hover and focus-within cancel it outright, so
   a pointer arriving at the bar restores it before it can be clicked and a
   keyboard tab into it can never land on something half visible. Ancestors of a
   hovered element match :hover, which is why this works even though .navwrap
   itself has pointer-events:none. */
/* Fading the CHILDREN, not .nav itself. opacity on a parent creates a stacking
   context and a child cannot be more opaque than it, so the exception below
   for the survey button was silently doing nothing until it was written this
   way. The capsule's own background, border and blur stay at full strength,
   which is what keeps the bar legible while it steps back. */
.is-down .nav > *{opacity:.55}
.is-down .nav > .nav-cta{opacity:1}
.navwrap:hover .nav > *,.navwrap:focus-within .nav > *{opacity:1}

.brand{display:flex;align-items:center;flex:0 0 auto;text-decoration:none;margin-right:6px}
/* The lockup is an image now, not a drawn SVG, so font-family means nothing
   here. The lockup is 4.5:1, rebuilt from David's artwork with CLIMATE and
   SHIELD on one line the way Flame Shield sets its wordmark. Stacked over
   three lines it was 2.83:1, and at the 50px this capsule allows that gave
   CLIMATE a cap height of about 9px against the 13 to 16 a bar wordmark
   needs. One line at 50px gives it 14. */
/* 6.5:1 since the lockup moved to Flame Shield's proportions on 11 September
   2026: the emblem shrank to 2.25 times the cap height and LONDON is tracked
   out between rules. At 40px the CLIMATE cap is 17.8px, larger than it was at
   50px (13.8px), so the lettering got MORE legible while the whole lockup got
   shorter. The width it gained is paid for by the Company dropdown. */
.logo{height:40px;width:auto;display:block}
.logo-sub{font-family:var(--font)}
/* The footer runs the owner's supplied artwork at 90px, which is where its
   wordmark finally reaches a readable size. See the note by LOGO(). */
.logo--ftr{height:54px}
/* The registered-office note under the footer address. Small and quiet: it is
   there so the line does not read as a shopfront, not to be read first. */
.ftr-note{display:block;font-style:normal;font-size:12.5px;line-height:1.45;
  color:var(--ink-3);margin-top:2px;max-width:34ch}

.navlinks{display:flex;align-items:center;gap:2px;margin-left:auto;list-style:none}
.navlinks > li{position:relative}
.navlinks > li > a{display:inline-flex;align-items:center;gap:5px;
  padding:11px 9px;border-radius:var(--r-pill);text-decoration:none;
  color:#DCE7F0;font-size:14.5px;font-weight:500;white-space:nowrap;
  transition:background .16s,color .16s}
.navlinks > li > a:hover,.navlinks .has-sub:hover > a{background:rgba(255,255,255,.1);color:#fff}
.navlinks a[aria-current=page]{color:#fff;background:rgba(255,255,255,.12)}
.chev{width:9px;height:6px;opacity:.6;transition:rotate .18s ease}
.has-sub:hover .chev{rotate:180deg}

/* ---- the dropdown ---------------------------------------------------------
   Rebuilt as a mega panel. The old one was a list of links with
   a caption, which is the default and reads as unfinished. What makes theirs
   work is three things, and all three are here: a tinted icon tile on every
   row so the eye can find a row by shape, a full sentence of plain English
   under every title, and a dark feature card in the same position in every
   panel so there is always somewhere to go if none of the rows is right.

   The panel is anchored to the item rather than the bar, and it is capped to
   the viewport, so it cannot push the document sideways. */
.sub{position:absolute;top:calc(100% + 14px);left:calc(-14px + var(--sub-x,0px));
  width:max-content;max-width:min(760px,calc(100vw - 32px));
  background:var(--paper);border:1px solid var(--line);border-radius:22px;
  box-shadow:0 34px 80px -30px rgba(17,26,34,.5);padding:14px;
  opacity:0;visibility:hidden;translate:0 -8px;
  transition:opacity .18s ease,translate .18s ease,visibility .18s}
/* An invisible bridge, so the pointer can cross the gap from the menu item to
   the panel without the panel closing under it on the way. */
.sub::before{content:"";position:absolute;top:-16px;left:0;right:0;height:18px}
.has-sub:hover .sub,.has-sub:focus-within .sub{opacity:1;visibility:visible;translate:0 0}
/* The last two items hang their panel from the right, so a panel is on the
   screen before main.js measures it, and with JavaScript off. */
.navlinks > .has-sub:nth-last-child(-n+2) .sub{left:auto;right:calc(-14px - var(--sub-x,0px))}
.sub-in{display:grid;grid-template-columns:minmax(0,1fr) 260px;gap:12px;align-items:stretch}
.sub-main{display:grid;align-content:start;gap:10px;padding:6px 4px 4px 6px}
.sub-intro{font-size:13px;line-height:1.5;color:var(--ink-2);max-width:52ch;
  padding:0 6px}
.mgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2px}

.mi{display:flex;align-items:flex-start;gap:11px;padding:10px;border-radius:14px;
  text-decoration:none;color:inherit;transition:background .14s}
.mi:hover{background:var(--ice-50)}
.mi-ic{flex:none;width:36px;height:36px;border-radius:11px;display:grid;
  place-items:center;background:var(--ice-100);color:var(--ice-700)}
.mi-ic svg{width:20px;height:20px}
.mi-t{display:flex;flex-direction:column;gap:2px;min-width:0}
.mi-t b{font-size:14.5px;font-weight:650;color:var(--ink);letter-spacing:-.01em}
.mi-t small{font-size:12.5px;line-height:1.4;color:var(--ink-2)}

/* Eleven place names do not each need a sentence. They need to be scannable. */
/* Twenty one boroughs since 11 September 2026: four columns and a wider
   panel, and the long names ("Barking and Dagenham") wrap rather than run
   into the next column. */
.sub--areas{max-width:min(920px,calc(100vw - 40px))}
.mareas{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:2px}
.marea{display:flex;flex-direction:column;gap:1px;padding:9px 11px;border-radius:12px;
  text-decoration:none;color:inherit;transition:background .14s}
.marea:hover{background:var(--ice-50)}
.marea b{font-size:14px;font-weight:600;color:var(--ink);line-height:1.25}
.marea small{font-size:11.5px;color:var(--ink-3)}
.marea--all{background:var(--paper-100)}
.marea--all b{color:var(--ice-700)}

/* The card at the end of every panel. Same shape every time, so it becomes a
   place people learn rather than a surprise. */
.mfeat{position:relative;isolation:isolate;overflow:hidden;display:flex;
  flex-direction:column;gap:6px;padding:18px 18px 16px;border-radius:18px;
  text-decoration:none;color:var(--on-dark-2);
  background:linear-gradient(140deg,var(--ink-900),var(--ink-800) 62%,var(--ice-900));
  transition:transform .2s ease}
.mfeat:hover{transform:translateY(-2px)}
.mfeat-ic{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;
  background:rgba(255,255,255,.12);color:var(--ice-300)}
.mfeat-ic svg{width:21px;height:21px}
.mfeat-tag{align-self:flex-start;background:var(--ice-300);color:var(--ink);
  font-size:10.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  padding:4px 9px;border-radius:var(--r-pill);margin-top:4px}
.mfeat b{color:#fff;font-family:var(--display);font-size:16px;letter-spacing:-.02em;
  line-height:1.2}
.mfeat-x{font-size:12.5px;line-height:1.45;color:var(--on-dark-2)}
.mfeat-cta{display:inline-flex;align-items:center;gap:6px;margin-top:2px;
  font-size:13px;font-weight:700;color:var(--ice-300)}
.mfeat-cta svg{width:15px;height:15px}


.nav-tel{display:inline-flex;align-items:center;gap:9px;padding:0 6px 0 14px;
  color:#fff;text-decoration:none;font-weight:600;font-size:14.5px;white-space:nowrap;
  border-left:1px solid rgba(216,232,244,.2);margin-left:8px}
.nav-tel svg{width:17px;height:17px;color:var(--ice-300)}
/* Height and the capsule's own padding have to agree, or the button reads as
   sitting on top of the bar rather than inside it. 68px capsule, 8px padding,
   so the button is 52px and the gap above and below is identical. */
.nav-cta{display:inline-flex;align-items:center;justify-content:center;
  min-height:52px;padding:0 20px;border-radius:var(--r-pill);
  background:var(--ice-400);color:var(--ink);font:700 14.5px/1 var(--font);
  text-decoration:none;white-space:nowrap;flex:0 0 auto;
  transition:background .16s,transform .16s,box-shadow .16s}
.nav-cta:hover{background:var(--ice-300);color:var(--ink);transform:translateY(-1px);box-shadow:var(--sh-accent)}

/* MADE LOUDER ON DESKTOP, at David's request. It is the only conversion
   control in the bar and it was sitting at the same visual weight as the seven
   navigation links beside it: same font size, no shadow, and the bar fades to
   0.55 on scroll down, which took it with it.

   Bigger type, a solid ring in the brand blue so it reads as a button rather
   than a tinted word, its own shadow, and it stays at full opacity while the
   rest of the bar dims. Only above 900px, because below that the bar hides it
   and the phone gets the call button instead. */
@media (min-width:901px){
  .nav-cta{font-size:15.5px;font-weight:800;letter-spacing:-.01em;padding:0 26px;
    box-shadow:0 10px 24px -10px rgba(30,134,188,.7),0 0 0 1px rgba(255,255,255,.22) inset}
  .nav-cta:hover{box-shadow:0 16px 32px -12px rgba(30,134,188,.85),0 0 0 1px rgba(255,255,255,.3) inset}
  /* The exception to the scroll-down fade lives up with the fade itself, so
     the two rules are read together. */
}

/* The three lines are absolutely positioned rather than laid out by a grid gap,
   because the open state rotates them into a cross and that maths only works if
   the distance between them is a number this file decides rather than one the
   layout happens to produce. */
.burger{display:none;position:relative;width:52px;height:52px;flex:0 0 auto;
  margin-left:auto;border:1px solid rgba(216,232,244,.22);cursor:pointer;padding:0;
  border-radius:var(--r-pill);background:rgba(255,255,255,.1);
  transition:background .16s,border-color .16s}
.burger:hover{background:rgba(255,255,255,.18)}
/* The three lines are centred on the button by maths rather than by a flex gap:
   52px button, 22px lines, so the left inset is (52-22)/2 = 15, and the rows sit
   at 19 / 25 / 31 so the middle one is exactly on centre and the outer two are
   6px away. The open state rotates them into a cross and that only lands if this
   file owns the spacing. */
.burger span{position:absolute;left:15px;width:22px;height:2px;background:#fff;
  border-radius:2px;transition:transform .22s ease,opacity .16s ease}
.burger span:nth-child(1){top:19px}
.burger span:nth-child(2){top:25px}
.burger span:nth-child(3){top:31px}
.burger[aria-expanded=true] span:nth-child(1){transform:translateY(6px) rotate(45deg)}
.burger[aria-expanded=true] span:nth-child(2){opacity:0}
.burger[aria-expanded=true] span:nth-child(3){transform:translateY(-6px) rotate(-45deg)}

/* The mobile menu.

   A paper card hanging from under the capsule over a scrim. It runs to the
   bottom of the screen now but stops 14px short of the edge on every side, so
   it still reads as a card on top of the page rather than a second screen.

   The call, WhatsApp and survey buttons that used to sit at the bottom are
   gone. They were duplicating three things the visitor already has within
   reach: the hero, the WhatsApp launcher in the corner, and the Contact row in
   this same list. A menu is for going somewhere.

   The labels are the other half of David's note. "Installation" on its own does
   not tell anybody what is being installed; every row now carries the same
   sentence the desktop dropdown uses, and the group heading is a real heading
   rather than a caption. */
.sheet{position:fixed;inset:0;z-index:99;display:flex;justify-content:center;
  align-items:flex-start;padding:calc(var(--nav-h) + 20px) 14px 14px;
  background:rgba(10,17,25,.52);
  backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);
  animation:sheet-in .18s ease both}
.sheet[hidden]{display:none}
.sheet > .wrap{width:100%;max-width:560px;margin:0;
  background:var(--paper);border:1px solid var(--line);
  border-radius:24px;box-shadow:0 40px 90px -30px rgba(10,17,25,.6);
  padding:6px 14px 14px;
  max-height:calc(100dvh - var(--nav-h) - 34px);
  overflow-y:auto;overscroll-behavior:contain;
  animation:sheet-card-in .22s cubic-bezier(.16,1,.3,1) both}
@keyframes sheet-in{from{opacity:0}to{opacity:1}}
@keyframes sheet-card-in{from{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:none}}
.sheet ul{list-style:none}
.sheet > .wrap > ul > li{padding:14px 0;border-bottom:1px solid var(--line)}
.sheet > .wrap > ul > li:last-of-type{border-bottom:0}
/* The group heading. It was 11.5px uppercase, which is a caption, and David is
   right that it disappeared: you read "Installation" with no idea what of. */
.sheet b{display:block;font-family:var(--display);font-size:19px;font-weight:800;
  letter-spacing:-.02em;color:var(--ink);margin:0 0 2px;padding:0 6px}
.sh-note{display:block;font-size:13px;line-height:1.45;color:var(--ink-2);
  padding:0 6px;margin-bottom:8px}
.sheet a{display:flex;align-items:center;gap:12px;text-decoration:none;
  padding:9px 6px;border-radius:14px;color:var(--ink);
  transition:background .14s}
.sheet a:active{background:var(--ice-50)}
.sh-ic{flex:none;width:38px;height:38px;border-radius:12px;display:grid;
  place-items:center;background:var(--ice-100);color:var(--ice-700)}
.sh-ic svg{width:21px;height:21px}
.sh-t{display:flex;flex-direction:column;gap:1px;min-width:0;
  font-size:15.5px;font-weight:650;line-height:1.25}
.sh-t small{font-size:12.5px;font-weight:400;line-height:1.4;color:var(--ink-2)}
.sh-top{font-size:17px;font-weight:700}
.sheet ul ul{display:grid;gap:2px}

/* The accordion. The heading row is the whole tap target, 60px and up, with
   the chevron turning as it opens. The rows fade in rather than appearing,
   which is the one piece of motion here and it is short. */
.sheet > .wrap > ul > li{padding:4px 0}
.sh-grp > summary{display:flex;align-items:center;gap:12px;padding:12px 6px;cursor:pointer;
  list-style:none;border-radius:14px;-webkit-tap-highlight-color:transparent}
.sh-grp > summary::-webkit-details-marker{display:none}
.sh-grp > summary:active{background:var(--paper-50)}
.sh-grp > summary:focus-visible{outline:2px solid var(--ice-600);outline-offset:2px}
.sh-h{flex:1;min-width:0}
.sh-gi{--mi-accent:var(--ice-500);transition:background .2s ease,color .2s ease}
.sh-grp[open] > summary .sh-gi{background:var(--ink);color:#fff;--mi-accent:#41C7FE}
.sh-h b{padding:0;margin:0 0 3px}
.sh-h .sh-note{padding:0;margin:0}
/* The sentence under a heading only once it is open: six one-line headings
   fit one screen, and the sentence is read when it is about to be used. */
.sh-grp:not([open]) .sh-note{display:none}
.sh-grp:not([open]) .sh-h b{margin:0}
.sh-chev{flex:none;width:30px;height:30px;padding:10px 9px;border-radius:50%;
  background:var(--paper-100);color:var(--ink-2);transition:transform .25s ease,background .2s ease,color .2s ease}
.sh-grp[open] > summary .sh-chev{transform:rotate(180deg);background:var(--ice-100);color:var(--ice-700)}
.sh-grp > ul{padding:2px 0 10px}

@keyframes shOpen{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
.sheet a[aria-current="page"]{background:var(--ice-50)}
.sheet a[aria-current="page"] .sh-t{color:var(--ice-700)}
/* Twenty one boroughs read better as two columns of names than one long list. */
.sh-areas{grid-template-columns:repeat(2,minmax(0,1fr))}
.sh-areas .sh-all{grid-column:1 / -1}
.sh-areas a{padding:9px 8px;background:var(--paper-50)}
.sh-areas .sh-t{font-size:14.5px}

/* AN OPEN GROUP LOOKS LIKE A GROUP. David, 12 September 2026: on a phone,
   opening a heading did not show that anything had appeared, because the
   links it revealed were the same shape as the headings under them: the same
   38px icon tile, the same bold title. Nielsen Norman Group's guidance for
   submenus inside a mobile menu is exactly this, that the lower level has to
   look different from the primary one, with indentation and colour; and the
   Australian government design system found in testing that a subtle
   collapsed state was simply overlooked.

   So three things change the moment a group opens, and each one alone would
   be enough:

     the group becomes a tinted panel, heading and links together, so the
       open section is one block with an edge, visibly different from the
       white closed headings under it;
     its links are white rows inset inside that panel, behind a vertical
       rule, which is the shape people read as "these belong to that";
     and the links lose the big icon tiles the headings have, keeping a
       small icon in the accent colour, so no link can be mistaken for the
       next heading down. */
.sh-grp{border-radius:18px;transition:background .2s ease}
.sh-grp[open]{background:var(--ice-50);padding-bottom:8px}
.sh-grp[open] > summary .sh-h b{color:var(--ice-800)}
.sh-grp[open] > summary{border-radius:18px 18px 0 0}
.sh-grp > ul{margin:0 8px 0 26px;padding:4px 0 4px 12px;border-left:2px solid var(--ice-200)}
.sh-grp > ul > li > a{background:#fff;border:1px solid var(--line);border-radius:12px;
  padding:10px 12px;gap:10px}
.sh-grp > ul > li > a:active{background:var(--ice-100)}
.sh-grp > ul > li > a .sh-ic{width:auto;height:auto;background:none;border-radius:0;color:var(--ice-600)}
.sh-grp > ul > li > a .sh-ic svg{width:18px;height:18px}
.sh-grp > ul > li > a .sh-t{font-size:15px;font-weight:600}
.sh-grp > ul > li > a[aria-current="page"]{border-color:var(--ice-500);background:#fff;box-shadow:inset 3px 0 0 var(--ice-600)}
.sh-grp > ul.sh-areas{gap:6px}
.sh-grp > ul.sh-areas > li > a{padding:9px 10px}
.sh-grp > ul{gap:6px}
/* The rows arrive one after another, 30ms apart, which is the cue that they
   are new rather than something that was there all along. */
@media (prefers-reduced-motion:no-preference){
  .sh-grp[open] > ul > li{animation:shRow .24s cubic-bezier(.2,.8,.3,1) both}
  .sh-grp[open] > ul > li:nth-child(2){animation-delay:.03s}
  .sh-grp[open] > ul > li:nth-child(3){animation-delay:.06s}
  .sh-grp[open] > ul > li:nth-child(4){animation-delay:.09s}
  .sh-grp[open] > ul > li:nth-child(5){animation-delay:.12s}
  .sh-grp[open] > ul > li:nth-child(n+6){animation-delay:.14s}
}
@keyframes shRow{from{opacity:0;transform:translateX(-6px)}to{opacity:1;transform:none}}
/* The scrim does not scroll the page behind it on iOS. The card itself still
   pans, because touch-action is only intersected up to the nearest scroll
   container and the card is one. */
.sheet{touch-action:none}
.sheet > .wrap{touch-action:pan-y}

/* ==========================================================================
   TEN YEARS, AND THE IMAGE WALL

   The shape comes from the Motion marquee David sent: copy on one side, two
   vertical belts of images on the other running in opposite directions behind a
   mask that fades all four edges. Ported to the site's own belt(), which drives
   scrollTop here instead of scrollLeft, so a thumb can still stop it.

   The mask is what makes it read as a window onto something continuous rather
   than as two columns that happen to be cropped. Without it the belt has hard
   edges and looks like a bug.
   ========================================================================== */
.tenyr{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,460px);
  gap:clamp(28px,4vw,64px);align-items:center}
.tenyr-copy > p{margin-top:14px;color:var(--ink-2);max-width:56ch}
.tenyr-copy .lede{color:var(--ink);font-weight:500}

.tenyr-facts{list-style:none;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:1px;background:var(--line);border:1px solid var(--line);
  border-radius:var(--r-panel);overflow:hidden;margin-top:clamp(22px,2.4vw,30px)}
.tenyr-facts li{background:var(--paper);padding:16px 18px;display:grid;gap:2px}
.tenyr-facts b{font-family:var(--display);font-weight:800;letter-spacing:-.04em;
  font-size:clamp(26px,2.4vw,34px);line-height:1;color:var(--ice-700)}
.tenyr-facts span{font-size:13.5px;line-height:1.4;color:var(--ink-2)}
.tenyr-cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:clamp(20px,2.2vw,28px)}

/* One service at a time, 11 September 2026. Four tabs; the panes share one
   grid cell so the block keeps the height of the longest and nothing below
   moves when it changes. While it is rotating on its own, a thin line fills
   under the chosen tab so it is plain that it will move on, and the moment
   anybody picks a tab it stops rotating for good. */
.tenyr-tabs{display:flex;gap:4px;margin-top:18px;padding:5px;width:max-content;max-width:100%;
  background:#fff;border:1px solid var(--line);border-radius:var(--r-pill)}
.tenyr-tab{position:relative;overflow:hidden;border:0;background:transparent;cursor:pointer;
  color:var(--ink-2);font:inherit;font-size:15px;font-weight:650;white-space:nowrap;
  padding:10px 18px;border-radius:var(--r-pill);transition:background .2s ease,color .2s ease}
.tenyr-tab:hover{color:var(--ink);background:var(--paper-50)}
.tenyr-tab:focus-visible{outline:3px solid var(--ice-500);outline-offset:2px}
.tenyr-tab[aria-selected="true"]{background:var(--ice-700);color:#fff}
.tenyr-tab i{position:absolute;left:16px;right:16px;bottom:5px;height:2px;border-radius:2px;
  background:rgba(255,255,255,.55);transform:scaleX(0);transform-origin:left;opacity:0}
.tenyr-tabs.is-auto .tenyr-tab[aria-selected="true"] i{opacity:1;animation:tyProg var(--ty-ms,7000ms) linear both}
.tenyr-tabs.is-paused .tenyr-tab i{animation-play-state:paused}
@keyframes tyProg{to{transform:scaleX(1)}}
.tenyr-panes{display:grid}
.tenyr-pane{grid-area:1 / 1;visibility:hidden;opacity:0;
  transition:opacity .35s ease,visibility 0s linear .35s}
.tenyr-pane.is-on{visibility:visible;opacity:1;transition:opacity .35s ease}
.tenyr-pane > p{margin-top:14px;color:var(--ink-2);max-width:56ch}
.tenyr-pane > .lede{color:var(--ink);font-weight:500}
.wall{transition:opacity .3s ease}
.wall.is-swap{opacity:.12}
@media (prefers-reduced-motion:reduce){ .tenyr-pane,.wall{transition:none} }
/* On a phone four pills in a row would scroll sideways, so they are a two by
   two block of large targets instead. */
@media (max-width:600px){
  .tenyr-tabs{display:grid;grid-template-columns:1fr 1fr;width:100%;border-radius:18px}
  .tenyr-tab{border-radius:13px;padding:12px 10px;font-size:14.5px}
}

/* The accreditation marks. White tiles on purpose: both ship on an opaque white
   ground, and NICEIC's counters are white and part of the drawing, so keying the
   white out would open holes in the letters. A badge in a white tile is also
   how every trade body asks for its mark to be shown. */
.accs{list-style:none;display:grid;gap:10px;margin-top:clamp(22px,2.4vw,30px);
  padding-top:clamp(20px,2.2vw,26px);border-top:1px solid var(--line)}
.accs > li{display:flex;align-items:center;gap:16px}
.accs img{flex:none;height:48px;width:auto;padding:9px 14px;background:#fff;
  border:1px solid var(--line);border-radius:12px;box-sizing:content-box}
.accs span{font-size:14px;line-height:1.45;color:var(--ink-2)}
.accs-more a{display:inline-flex;align-items:center;gap:7px;font-size:13.5px;
  font-weight:600;color:var(--ice-700);text-decoration:none}
.accs-more a:hover{text-decoration:underline}
.accs-more svg{width:15px;height:15px}
/* On the accreditation page the same list runs wider and carries a full
   sentence, so the mark sits at the top of the text rather than centred on it. */
.accs--page{gap:18px;border-top:0;padding-top:0;margin-top:18px}
.accs--page > li{align-items:flex-start}
.accs--page img{height:44px}
.accs--page span{font-size:15px;color:var(--ink-2);max-width:62ch}
.accs--page strong{color:var(--ink)}



.wall{display:grid;grid-template-columns:1fr 1fr;gap:14px;height:clamp(420px,42vw,560px);
  -webkit-mask:linear-gradient(180deg,transparent,#000 14%,#000 86%,transparent);
  mask:linear-gradient(180deg,transparent,#000 14%,#000 86%,transparent)}
.wall-col{min-height:0}
/* Not scrollable by hand, on David's call. `overflow: hidden` still lets the
   belt drive scrollTop from script, so the columns keep moving; what it removes
   is the visitor being able to grab them. That matters here in a way it does
   not on the services belt: this is decoration beside the copy, and a decorative
   panel that swallows a scroll gesture on a phone is a trap. The services belt
   stays grabbable, because there the cards are the content. */
/* `overflow: hidden` is what lets the page scroll through this. The container
   has nothing of its own to scroll, so a vertical drag starting on the photos
   is handed straight to the document, which is what David asked for: on a phone
   these sit in the middle of the section and a panel that swallowed the scroll
   would trap the thumb. `touch-action: pan-y` says so explicitly rather than
   relying on the default, and `pointer-events: none` on the children stops a
   drag being read as a drag on an image.

   The belt still moves them: scrollTop is settable from script whatever the
   overflow value is. */
.wall-view{height:100%;overflow:hidden;touch-action:pan-y}
.wall-view *{pointer-events:none}
.wall-view::-webkit-scrollbar{display:none}
.wall-track{display:flex;flex-direction:column}
.wall-group{display:flex;flex-direction:column;gap:14px;flex:none;padding-bottom:14px}
.wall figure{margin:0;border-radius:16px;overflow:hidden;background:var(--paper-200);
  box-shadow:var(--sh-sm)}
.wall img{display:block;width:100%;height:auto;aspect-ratio:9/16;object-fit:cover}

/* The three arguments, as disclosures rather than columns. Same chevron and
   hairline treatment as the FAQ, so opening one behaves the way opening one
   anywhere on this site does. */
.says{margin-top:clamp(26px,3vw,40px);display:grid;gap:1px;
  background:var(--line);border:1px solid var(--line);
  border-radius:var(--r-panel);overflow:hidden}
.say{background:var(--paper)}
.say > summary{list-style:none;cursor:pointer;display:flex;align-items:center;
  justify-content:space-between;gap:14px;padding:16px 18px;
  font-family:var(--display);font-weight:800;letter-spacing:-.02em;
  font-size:var(--h-card);color:var(--ink);transition:background .14s}
.say > summary::-webkit-details-marker{display:none}
.say > summary::after{content:"";flex:0 0 auto;width:9px;height:9px;
  border-right:2.2px solid var(--ice-600);border-bottom:2.2px solid var(--ice-600);
  rotate:45deg;translate:0 -2px;transition:rotate .2s ease}
.say[open] > summary::after{rotate:225deg;translate:0 2px}
.say > summary:hover{background:var(--ice-50)}
.say > p{padding:0 18px 18px;font-size:14.5px;line-height:1.62;color:var(--ink-2);
  max-width:78ch}

/* ==========================================================================
   BUTTONS
   Pill only. Three variants and no others: solid accent, WhatsApp green
   (because it is a brand colour and reads as the product it opens), and a
   stroked ghost that adapts to the surface it sits on.
   ========================================================================== */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;
  min-height:52px;padding:0 26px;border:1.5px solid transparent;border-radius:var(--r-pill);
  background:var(--ice-400);color:var(--ink);font:700 15px/1 var(--font);text-decoration:none;
  cursor:pointer;white-space:nowrap;
  transition:background .16s ease,transform .16s ease,box-shadow .16s ease}
.btn:hover{background:var(--ice-300);color:var(--ink);transform:translateY(-2px);box-shadow:var(--sh-accent)}
.btn:active{transform:translateY(0)}
.btn--lg{min-height:58px;padding:0 30px;font-size:16px}
.btn--sm{min-height:44px;padding:0 18px;font-size:14px}
.btn--wide{width:100%}
/* The one dark fill left on the site, so it is the one button that keeps white
   text. `.btn` sets ink now, and inheriting that put dark text on dark green at
   3.65:1. White on this green is 4.71:1. */
.btn--wa{background:#16853F;color:#fff}
.btn--wa:hover{color:#fff}
.btn--wa:hover{background:#0F6D32;box-shadow:0 20px 46px -20px rgba(22,133,63,.5)}
.btn--wa svg{width:18px;height:18px}
/* The hero's second button, the one that replaced "Call". An arrow, because
   it goes somewhere on the page rather than opening a conversation. */
.btn--next svg{width:16px;height:16px;flex:none;transition:transform .2s ease}
.btn--next:hover svg{transform:translateX(3px)}
.btn--ghost{background:transparent;color:var(--ice-700);border-color:var(--line-2)}
.btn--ghost:hover{background:var(--ice-50);border-color:var(--ice-500);
  color:var(--ice-700);box-shadow:none}
.hero .btn--ghost{color:#fff;border-color:rgba(255,255,255,.34)}
.hero .btn--ghost:hover{background:rgba(255,255,255,.1);
  border-color:#fff;color:#fff}
/* Kept only because the heating pages ask for it by name. It used to be the
   amber button that marked heating; now that action itself is ember there is
   nothing left for it to say, and --warm as a fill fails contrast anyway:
   white on #E1562B is 3.6:1. So it is the ordinary button, and the heating
   pages are marked by the tint of the card around it instead. */
/* Warm is a tint and never a fill under text, so the heating pages use the
   ordinary button and are marked by the card around it. */
.btn--warm{background:var(--ice-400)}
.btn--warm:hover{background:var(--ice-300);box-shadow:var(--sh-accent)}

/* ==========================================================================
   THE HERO
   Split. Copy on the left, the permission check on the right, which is where
   David wants it: it is the mechanism, and burying it in its own section below
   the fold made it look like an afterthought.

   The left column keeps four elements and no more. What is deliberately not
   here any more is the strip of company facts that used to sit under the
   headline. Company number, NICEIC, Gas Safe and MCS belong in the footer and
   on the accreditation page, not under a hero.
   ========================================================================== */
/* The two columns are `align-items: start`, not centre.

   Centred was the cause of the dead space David saw. The permission check on
   the right is a tall form and the copy on the left is a headline, a line and
   two buttons; centring the short column against the tall one banks the whole
   difference as blank space above and below the headline, which then reads as
   the hero starting a third of the way down. Aligned to the top, the headline
   sits where the eye lands and the extra height belongs to the form, which is
   the thing that actually needs it. */
.hero{position:relative;isolation:isolate;background:var(--ink-950);color:#fff;
  /* +46, not +18. Measured, the form panel's top edge sat 2px below the
     navigation capsule: technically not overlapping and visually stuck to it.
     This gives 30px of air. The copy column is centred against the form so it
     follows without being pushed down. */
  padding-top:calc(var(--nav-h) + 46px);padding-bottom:clamp(38px,4.4vw,62px);overflow:clip}
.hero-bg{position:absolute;inset:0;z-index:-2}
.hero-bg img{width:100%;height:100%;object-fit:cover;object-position:58% 44%}

/* THE HERO VIDEO.

   It sits on top of the still, not instead of it. The still paints immediately
   and is what the largest contentful paint measures; the video arrives later,
   already playing, and fades in over the top. If it never arrives, or the
   visitor has asked for less motion, or the connection is metered, the still is
   the whole design and nobody can tell anything is missing.

   `object-fit: cover` on both, so the two are framed the same way and the fade
   is a fade rather than a jump. */
.hero-vid,.phero-vid{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:58% 44%;
  opacity:0;transition:opacity .9s ease;pointer-events:none}
.hero-vid[data-on],.phero-vid[data-on]{opacity:1}
/* THE VEIL, lightened 2026-09-05.

   It used to be two stacked gradients covering the whole frame, and between
   them the footage was barely visible. David: let the clip be understood.

   The darkness now goes where the text is and nowhere else. The copy sits in
   the left 45%, so the horizontal gradient is heavy there and gone by 62%; the
   right half, where the form sits on its own opaque card, is left almost clear
   and that is where most of the clip actually reads. The vertical layer is now
   only a small lift at the top and a foot at the bottom, to stop the section
   ending on a hard edge.

   The floor is not a guess. The binding constraint is the sub-paragraph, 17px
   in #D2E2EE, which needs 4.5:1. Against the brightest frame in the loop that
   works out at about 0.80 alpha where that line sits, so the gradient holds
   0.84 to 0.66 across the copy column and only falls away past it. The 54px
   headline is large text and needs 3:1, which it clears everywhere. */
.hero-veil{position:absolute;inset:0;z-index:-1;
  background:
    linear-gradient(180deg,rgba(10,17,25,.34) 0%,rgba(10,17,25,0) 26%,rgba(10,17,25,0) 64%,rgba(10,17,25,.62) 100%),
    linear-gradient(97deg,rgba(10,17,25,.84) 0%,rgba(10,17,25,.78) 26%,rgba(10,17,25,.66) 42%,rgba(10,17,25,.22) 56%,rgba(10,17,25,0) 66%)}

/* 530, not 470. "Air conditioning installation" is the longest option in the
   job select and at 470 the field was half of 470 minus a gap, which truncated
   it. A select that hides its own longest option is a select nobody trusts. */
.hero-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,530px);
  gap:var(--gap);align-items:start}
/* Centred against the form, which is what David asked for, but only up to a
   point: `align-items: center` on the grid was the original blank-space bug
   because it banked the whole height difference above and below the headline.
   Centring just this column with a cap means the copy sits in the middle of the
   form beside it while it is close in height, and stops drifting down once the
   form is much taller. */
.hero-copy{align-self:center;margin-block:clamp(0px,2vw,34px)}
/* 22ch, not 17. At 17 the headline broke into three very uneven lines because
   the measure was too narrow for `balance` to have any choices. */
.hero h1{color:#fff;font-size:clamp(36px,4.2vw,54px);max-width:22ch;text-wrap:balance}
.hero-copy > p{color:#D2E2EE;font-size:clamp(16px,.4vw + 15px,18px);max-width:44ch;margin-top:16px}
/* THE HERO BUTTONS.

   They were flat pills, and against a photograph a flat pill has nothing
   holding it to the page. The reference design's hero buttons carry a real
   shadow and a thick ring, and that is what makes them read as objects rather
   than as coloured rectangles. Three things here do it:

     a 2px ring in the button's own colour, lightened, so the edge survives
     whatever is behind it in the photograph;
     an inset highlight along the top, which is what makes a solid fill look
     like it has a surface;
     a shadow tinted to the fill rather than to black.

   Hero only. Buttons in the body of the page sit on paper and need none of it. */
.hero-cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:24px}
.hero-cta .btn{min-height:58px;padding:0 30px;font-weight:800;
  border:2px solid rgba(255,255,255,.35);
  box-shadow:0 10px 26px -10px rgba(6,20,32,.75),inset 0 1px 0 rgba(255,255,255,.45)}
.hero-cta .btn:not(.btn--wa){border-color:var(--ice-200);
  box-shadow:0 12px 30px -12px rgba(30,134,188,.75),inset 0 1px 0 rgba(255,255,255,.55)}
.hero-cta .btn:not(.btn--wa):hover{border-color:#fff}
.hero-cta .btn--wa{border-color:rgba(120,220,160,.55);
  box-shadow:0 12px 30px -12px rgba(22,133,63,.8),inset 0 1px 0 rgba(255,255,255,.28)}
.hero-cta .btn--wa:hover{border-color:rgba(150,235,185,.85)}
.hero-cta .btn:hover{transform:translateY(-2px)}
.hero-cta .btn:active{transform:translateY(0) scale(.985)}

/* One breakpoint, two audiences. See the note in the home page markup. */
.only-narrow{display:none}


/* The hero form. It replaced the permission check on 2026-09-05: the hero of a
   company that sells installations should ask for the installation. */
.btn--jump{display:none}
.hero-form{background:var(--paper);border-radius:var(--r-panel);
  box-shadow:0 34px 80px -34px rgba(6,16,24,.7);overflow:hidden}
.hero-form-hd{padding:clamp(18px,1.8vw,24px) clamp(18px,1.8vw,26px) 0}
.hero-form-hd h2{font-size:var(--h-panel);color:var(--ink)}
.hero-form-hd p{margin-top:7px;font-size:13.5px;line-height:1.5;color:var(--ink-2)}
.lead--hero{padding:clamp(16px,1.6vw,22px) clamp(18px,1.8vw,26px) clamp(18px,1.8vw,24px)}
/* Nothing follows the submit button in a hero form, so the card needs its own
   bottom padding rather than borrowing the foot note's. */
.lead--hero .lead-foot[hidden]{display:none}
.lead--hero{padding-bottom:clamp(20px,2vw,26px)}
/* The job select carries the longest string in the form, so in the hero panel
   its row goes to one column and the select gets the full width of the panel.

   A postcode is eight characters and the job is a sentence, so the row is split
   0.62 to 1 rather than in half. That is enough for "Air conditioning
   installation" plus the select's own arrow with room to spare, and it keeps
   both fields on one line.

   `:nth-of-type(2)` was the obvious way to say "the second row" and it was
   wrong: nth-of-type counts among siblings of the same TAG, and the honeypot
   `.lead-hp` is a div sitting before the rows, so it took slot one and the rule
   landed on the name and phone row instead. `.lead-row + .lead-row` says the
   thing that was actually meant, which is the row that follows a row. */
.lead--hero .lead-row + .lead-row{grid-template-columns:minmax(0,0.62fr) minmax(0,1fr)}


/* ==========================================================================
   BRAND STRIP
   Logos only. David's rule: do not print the name of an accreditation unless
   there is a real mark to print with it. We have manufacturer logos and we do
   not have NICEIC, Gas Safe or MCS artwork, so those claims live in the footer
   and on the accreditation page as text in context, and this strip shows the
   five marks we actually hold files for.
   ========================================================================== */
/* The manufacturer marks.

   A continuous belt, seamless, driven by scrollLeft so a thumb can push it or
   hold it still. See the long note on `belt()` in main.js.

   No card, no border, no filter. The marks all carry a real alpha channel now,
   so they sit straight on the band in their own colours, which is what a wall
   of manufacturer artwork is supposed to look like. Fading edges rather than
   hard ones, because a belt that stops dead at the container edge reads as a
   row that has been cut off. */
.marks{background:var(--paper-50);border-block:1px solid var(--line);
  padding-block:clamp(28px,3.2vw,42px)}
/* The band carries a section heading in the page's own size and shape, not a
   caption. It used to have a paragraph under it explaining that we are not tied
   to one manufacturer, which is true and belongs on a service page rather than
   above a logo belt: a belt of five wordmarks needs a label, not an argument. */
.marks-belt{margin-top:clamp(18px,2.2vw,28px);
  -webkit-mask:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
  mask:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent)}
.marks-view{overflow-x:auto;overflow-y:hidden;scrollbar-width:none;
  -ms-overflow-style:none;overscroll-behavior-x:contain}
.marks-view::-webkit-scrollbar{display:none}
.marks-track{display:flex;width:max-content}
.marks-group{display:flex;align-items:center;flex:none}
.marks-i{display:grid;place-items:center;flex:none;
  padding-inline:clamp(26px,3.4vw,58px)}
.marks-i img{height:clamp(30px,2.8vw,40px);width:auto;max-width:none;
  object-fit:contain}
/* The accreditations belt: on white, and bigger, because a badge is a
   drawing with small print in it rather than a wordmark. */
.marks--accs{background:#fff}
.marks--accs .marks-i img{height:clamp(34px,3.2vw,46px)}
.marks--accs .marks-i--badge img{height:clamp(64px,6vw,88px)}


/* ==========================================================================
   THE PERMISSION CHECK
   A white panel inside the dark hero. It is the one thing on this site no
   competitor has, so it gets the strongest position on the page.
   ========================================================================== */
/* The check moved off the home page hero and onto the planning page, where it
   sits in a full width band rather than a 470px column. Without a cap it would
   stretch to 1240px and the three questions would look lost in it. */
.pcheck-wrap{max-width:560px;margin-inline:auto}
.pcheck-wrap .pcheck{box-shadow:var(--sh)}

.pcheck{background:#fff;border-radius:var(--r-panel);box-shadow:var(--sh-lg);
  color:var(--ink);overflow:hidden;border:1px solid rgba(255,255,255,.5)}
.pcheck-hd{padding:26px 28px 0}
.pcheck-hd h2{font-size:var(--h-panel);color:var(--ink)}
.pcheck-hd p{color:var(--ink-2);font-size:14.5px;margin-top:8px}
.pcheck-body{padding:22px 28px 28px;display:grid;gap:20px}
.fld{display:grid;gap:10px}
.fld > label,.fld > legend{font-size:14px;font-weight:600;color:var(--ink);padding:0}
fieldset{border:0;margin:0;padding:0;display:grid;gap:10px}
input[type=text]{width:100%;min-height:52px;padding:0 16px;border:1.5px solid var(--line-2);
  border-radius:var(--r-input);background:#fff;color:var(--ink);
  font:600 16px/1 var(--font);text-transform:uppercase;
  transition:border-color .15s,box-shadow .15s}
input[type=text]::placeholder{color:var(--ink-3);text-transform:none;font-weight:400}
input[type=text]:focus{border-color:var(--ice-600);box-shadow:0 0 0 4px rgba(46,138,128,.15);outline:none}

.seg{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.seg label{position:relative;display:block}
.seg input{position:absolute;inset:0;opacity:0;cursor:pointer;margin:0}
.seg span{display:flex;align-items:center;justify-content:center;text-align:center;
  min-height:50px;padding:9px 12px;border:1.5px solid var(--line-2);border-radius:var(--r-input);
  font-size:14.5px;font-weight:500;cursor:pointer;background:#fff;color:var(--ink);
  transition:border-color .14s,background .14s,color .14s,transform .14s}
.seg input:hover + span{border-color:var(--ice-400)}
.seg input:checked + span{border-color:var(--ice-600);background:var(--ice-50);
  color:var(--ice-700);font-weight:700}
.seg input:focus-visible + span{outline:3px solid var(--ice-500);outline-offset:2px}
.seg--wide{grid-template-columns:1fr}
.pcheck-foot{font-size:13px;color:var(--ink-3);text-align:center}
/* ==========================================================================
   THE STEPPER
   Every calculator uses this below 720. One question on screen, a progress bar
   and a count, Back and Next. Choosing a radio advances on its own, so Next is
   only there for the steps that end in typing rather than tapping.
   ========================================================================== */
.stepnav{display:grid;gap:12px}
.stepnav[hidden]{display:none}
.stepnav-bar{height:4px;border-radius:99px;background:var(--paper-200);overflow:hidden}
.stepnav-bar i{display:block;height:100%;border-radius:99px;background:var(--ice-600);
  width:12.5%;transition:width .28s cubic-bezier(.16,1,.3,1)}
.stepnav-row{display:flex;align-items:center;justify-content:space-between;gap:12px}
.stepnav-n{font-size:13px;font-weight:600;color:var(--ink-2);
  font-variant-numeric:tabular-nums}
.stepbtn{min-height:44px;padding:0 18px;border-radius:var(--r-pill);cursor:pointer;
  border:1.5px solid var(--line-2);background:#fff;color:var(--ink);
  font:700 14px var(--font);transition:border-color .15s,background .15s,color .15s}
.stepbtn:hover:not(:disabled){border-color:var(--ice-500);color:var(--ice-700)}
.stepbtn:disabled{opacity:.42;cursor:default}
.stepbtn[hidden]{display:none}
.stepbtn--next{background:var(--ice-400);border-color:var(--ice-400);color:var(--ink)}
.stepbtn--next:hover{background:var(--ice-300);border-color:var(--ice-300);color:var(--ink)}

.verdict{padding:24px 28px;display:grid;gap:15px;border-top:1px solid var(--line)}
.verdict[hidden]{display:none}
.v-ok{background:var(--ok-50)}
.v-warn{background:var(--warm-50)}
.verdict h3{font-size:var(--h-card)}
.v-ok h3{color:var(--ok)}
.v-warn h3{color:var(--warm-ink)}
.verdict p{font-size:15px;color:var(--ink-2)}
.v-warn p{color:#7A4E14}
.cond{width:100%;font-size:14px;background:#fff;border-radius:var(--r-input);overflow:hidden}
.cond caption{text-align:left;font-size:13.5px;font-weight:600;color:var(--ink);padding-bottom:9px}
.cond th,.cond td{text-align:left;padding:11px 13px;border-bottom:1px solid var(--line)}
.cond thead th{font-size:12.5px;color:var(--ink-2);font-weight:600;background:var(--paper-50)}
.cond tbody tr:last-child td{border-bottom:0}
.cond td:last-child,.cond th:last-child{text-align:right}
.mark{font-weight:700;font-size:13.5px}
.mark--y{color:var(--ok)}
.mark--q{color:var(--hold)}
.verdict-cta{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.todo{background:#fff;border:1.5px dashed var(--line-2);color:var(--ink-2);
  padding:14px 16px;font-size:14px;border-radius:var(--r-input)}
.todo b{color:var(--ink)}

/* ==========================================================================
   PAGE HERO, for everything that is not the homepage
   ========================================================================== */
.phero{position:relative;isolation:isolate;background:var(--ink-950);color:#fff;
  padding-top:calc(var(--nav-h) + 62px);padding-bottom:clamp(46px,5vw,72px);overflow:clip}
.phero-bg{position:absolute;inset:0;z-index:-2}
.phero-bg img{width:100%;height:100%;object-fit:cover;object-position:60% 40%}
.phero-vid{object-position:60% 40%}
/* Same principle as .hero-veil above, and the page hero needs a little more
   because its copy column runs wider and its lede is longer. */
.phero-veil{position:absolute;inset:0;z-index:-1;
  background:
    linear-gradient(180deg,rgba(10,17,25,.3) 0%,rgba(10,17,25,0) 30%,rgba(10,17,25,0) 66%,rgba(10,17,25,.55) 100%),
    linear-gradient(97deg,rgba(10,17,25,.86) 0%,rgba(10,17,25,.8) 30%,rgba(10,17,25,.66) 46%,rgba(10,17,25,.26) 60%,rgba(10,17,25,.04) 72%)}
.phero-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,400px);
  gap:var(--gap);align-items:start}
.phero h1{color:#fff;max-width:17ch}
/* The dark heroes all need the light lede. .phero and .thero had it; .hero,
   which is what /areas/ and /contact/ use, did not, so those two pages ran the
   global .lede colour, --ink-2, which is a dark grey meant for a white page.
   2.7 to 1 on the dark ground. Found by walking every text node on the page
   and comparing it with the colour actually behind it. */
.phero .lede{color:#D2E2EE;margin-top:18px;max-width:50ch}
/* The two page heroes with no photograph behind them, /areas/ and /contact/.
   A dark ground with nothing on it was exactly the full-width blue David asked
   to be rid of, so they are white, with the card beside them given an edge. */
.hero--page{background:#fff;color:var(--ink);border-bottom:1px solid var(--line)}
.hero--page h1{color:var(--ink)}
.hero--page .hl{color:var(--ice-600)}
.hero--page .lede{color:var(--ink-2);margin-top:18px;max-width:50ch}
.hero--page .hero-price{color:var(--ink-2);margin-top:16px;font-size:15px}
/* The copy and the card side by side. .hero-in--page never had a rule of its
   own, so the card fell under the copy at full width on every screen. */
.crumbs + .hero--page{padding-top:clamp(30px,3.4vw,52px);padding-bottom:clamp(40px,4.4vw,64px)}
.hero-in--page{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,540px);
  gap:var(--gap);align-items:start}
@media (max-width:1000px){ .hero-in--page{grid-template-columns:minmax(0,1fr)} }
.hero--page .quotecard,.hero--page .hero-form{border:1px solid var(--line);box-shadow:var(--sh)}
.hero--page .btn--ghost{color:var(--ice-700);border-color:var(--line-2)}
.phero-cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:28px}

/* TICK LISTS UNDER A HEADING: THE RULE.
   David, 6 September 2026: stop adding bullet points under text and under
   headers, there are too many of them, they kill the phone and add a pile of
   scroll. He is right, and the measurement backs him: on a 375px screen the
   four ticks in the installation hero were 209 pixels, a quarter of the
   screen, and they pushed the call buttons to y=736 and the form to y=888,
   both off the first screen. On most pages two of the four repeated the
   sentence directly above them.

   So: at most three anywhere, and none of them on a phone. The words are still
   in the HTML for anything reading the page rather than looking at it; what a
   phone gets instead is the heading, the sentence and the button, which is the
   order somebody actually uses. */
.ticks{list-style:none;display:grid;gap:11px;margin-top:26px;max-width:52ch}
@media (max-width:860px){
  .phero .ticks,.thero .ticks,.hero .ticks{display:none}
}
.ticks li{position:relative;padding-left:30px;font-size:15.5px;color:#D5E5F1}
.ticks li::before{content:"";position:absolute;left:0;top:3px;width:19px;height:19px;
  border-radius:50%;background:rgba(99,181,170,.18)}
.ticks li::after{content:"";position:absolute;left:6px;top:9px;width:7px;height:4px;
  border-left:2px solid var(--ice-300);border-bottom:2px solid var(--ice-300);rotate:-45deg}
.ticks b{color:#fff;font-weight:700}
.band--tint .ticks li,.band .ticks li,.hero--page .ticks li,.lhead .ticks li{color:var(--ink)}
.band--tint .ticks li::before,.band .ticks li::before,.hero--page .ticks li::before,.lhead .ticks li::before{background:var(--ice-50)}
.band--tint .ticks li::after,.band .ticks li::after,.hero--page .ticks li::after,.lhead .ticks li::after{border-color:var(--ice-600)}
.hero--page .ticks b,.lhead .ticks b{color:var(--ink)}

/* The survey card that sits beside a page hero. */
/* The hero panel on every page that is not the home page. It is the survey form
   in a white card, the same shape as the home page's, so a person who has seen
   one has seen them all. */
/* THE CARD OWNS ITS FOREGROUND. It is a light panel sitting inside a dark
   hero, so anything dropped into it inherits the hero's white unless a rule
   catches it. Only .quotecard-hd and .quotecard .lead did, which meant the two
   cards written by hand in misc.mjs rendered white on white: the whole "Based
   in N14" card on /areas/ was invisible, heading, paragraph and all three
   steps, and on /contact/ the heading, the intro and the closing line were.
   David found the second one.

   Setting the colour on the card itself is the fix that also covers whatever
   somebody puts in the next one. */
.quotecard{background:var(--paper);color:var(--ink);border-radius:var(--r-panel);
  box-shadow:0 34px 80px -34px rgba(6,16,24,.7);overflow:hidden;align-self:start}
.quotecard-hd{padding:clamp(18px,1.8vw,24px) clamp(18px,1.8vw,26px) 0}
.quotecard-hd h2{font-size:var(--h-panel);color:var(--ink)}
.quotecard-hd p{margin-top:7px;font-size:13.5px;line-height:1.5;color:var(--ink-2)}
.quotecard .lead{padding:clamp(16px,1.6vw,22px) clamp(18px,1.8vw,26px) clamp(18px,1.8vw,24px)}
.quotecard .lead-row + .lead-row{grid-template-columns:minmax(0,0.62fr) minmax(0,1fr)}

/* The numbered steps on the areas card. They had no rule at all, so they were
   a browser-default <ol> in white on white: invisible and unstyled at once. */
.qsteps{list-style:none;counter-reset:q;display:grid;gap:12px;
  padding:0 clamp(18px,1.8vw,26px) clamp(4px,0.6vw,8px);margin:14px 0 0}
.qsteps li{counter-increment:q;position:relative;padding-left:34px;
  display:grid;gap:2px}
.qsteps li::before{content:counter(q);position:absolute;left:0;top:1px;
  width:24px;height:24px;border-radius:50%;display:grid;place-items:center;
  background:var(--ice-50);color:var(--ice-700);font:800 12.5px var(--display)}
.qsteps b{font-size:14.5px;color:var(--ink);font-weight:700}
.qsteps span{font-size:13px;color:var(--ink-2);line-height:1.45}
.quotecard > .btn{margin:16px clamp(18px,1.8vw,26px) clamp(18px,1.8vw,24px);
  width:calc(100% - clamp(18px,1.8vw,26px) * 2)}

/* The line under the form on the contact card. */
.qfoot{padding:0 clamp(18px,1.8vw,26px) clamp(18px,1.8vw,24px);
  font-size:12.5px;color:var(--ink-3);line-height:1.5}

/* ==========================================================================
   TOWN HERO
   The borough pages. A real photograph of the place, the headline over it, and
   the form beside it, which is the shape David asked for. The photograph is
   already graded down at build time so one scrim works across eleven images by
   eleven different photographers.
   ========================================================================== */
.thero{position:relative;isolation:isolate;background:var(--ink-950);color:#fff;
  padding-top:calc(var(--nav-h) + 22px);padding-bottom:clamp(38px,4.2vw,60px);overflow:clip}
.thero-bg{position:absolute;inset:0;z-index:-2}
.thero-bg img{width:100%;height:100%;object-fit:cover;object-position:50% 42%}
.thero-veil{position:absolute;inset:0;z-index:-1;
  background:
    linear-gradient(180deg,rgba(10,17,25,.6) 0%,rgba(10,17,25,.42) 40%,rgba(10,17,25,.9) 100%),
    linear-gradient(100deg,rgba(10,17,25,.92) 0%,rgba(10,17,25,.68) 44%,rgba(10,17,25,.2) 78%)}
.thero-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,412px);
  gap:var(--gap);align-items:start}
.thero h1{color:#fff;font-size:clamp(34px,4vw,54px);max-width:14ch}
.thero .lede{color:#D2E2EE;margin-top:16px;max-width:48ch}
.thero-cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:26px}

.thero-form{background:#fff;color:var(--ink);border-radius:var(--r-panel);
  padding:clamp(20px,2vw,26px);box-shadow:var(--sh-lg);display:grid;gap:12px}
.thero-form h2{font-size:var(--h-panel)}
.thero-form > p{font-size:14px;color:var(--ink-2)}

/* Attribution is a condition of the CC BY-SA licence most of these carry, not
   decoration. Do not remove it. */
.thero-credit{position:relative;z-index:1;margin-top:26px;font-size:11.5px;
  color:rgba(221,230,237,.55);text-align:right;
  width:min(1240px,100% - var(--pad)*2);margin-inline:auto}

/* ==========================================================================
   LEAD FORM
   Posts to /api/enquiry. Works with JavaScript off; the enhancement is only
   that a fetch keeps the visitor on the page.
   ========================================================================== */
.lead{display:grid;gap:12px}
.lead-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.lead-row{display:grid;grid-template-columns:1fr 1fr;gap:10px}
/* On a page that knows what the job is, "What do you need?" is gone and the
   postcode is the only thing left in that row. Without this it would sit in a
   half-width column with a hole beside it. */
.lead-row--one{grid-template-columns:1fr}

/* THE TWO SCREENS.
   A step is a grid so it inherits the form's own gap; without display:grid the
   rows would sit flush against each other the moment the stepper turns on. */
.lead-step{display:grid;gap:12px}
.lead-branch{display:grid;gap:12px}
.lead-branch[hidden]{display:none}
.lead-branch fieldset[hidden]{display:none}

/* The stepper furniture in a form is smaller than in the price configurator:
   it is navigation for two screens, not the spine of an eight-question tool. */
.stepnav--lead{margin-top:2px}
.stepnav--lead .stepnav-bar{height:3px}
.stepnav--lead .stepbtn{min-height:40px;padding:0 16px;font-size:13.5px}
.stepnav--lead .stepnav-n{font-size:12.5px}

/* THE BUTTON. David: make it stand out more. It was the same weight as a
   secondary link on a card that has one job. Deeper fill, more height, a shadow
   in the brand's own blue so it lifts off the panel, and it presses. */
.btn--go{min-height:54px;font-size:16.5px;letter-spacing:-.01em;
  background:var(--ice-500);border-color:var(--ice-500);color:#062031;
  box-shadow:0 12px 26px -12px rgba(30,134,188,.75),0 2px 0 0 rgba(255,255,255,.35) inset}
.btn--go:hover{background:var(--ice-400);border-color:var(--ice-400);
  box-shadow:0 16px 30px -12px rgba(30,134,188,.85),0 2px 0 0 rgba(255,255,255,.35) inset}
.btn--go:active{transform:translateY(1px);
  box-shadow:0 8px 18px -12px rgba(30,134,188,.7)}

/* The way out to the calculator, for somebody who wants the number before the
   conversation. Deliberately a link and not a button: it must not compete with
   the thing above it. */
.lead-calc{display:block;margin-top:10px;text-align:center;font-size:13px;
  font-weight:600;color:var(--ice-700);text-decoration:none}
.lead-calc:hover{text-decoration:underline}
/* No dark-panel variant. Every form on the site sits on white or paper:
   .quotecard is --paper, .ctacard-form is #fff, and the hero card is white
   too. The first version assumed the hero form was on the dark ground and
   painted this ice-300, which is 1.7 to 1 on white and all but invisible. */
.lead .fld > label{font-size:13px;font-weight:600;color:var(--ink)}
.lead input[type=text],.lead input[type=tel],.lead input[type=email],
.lead select,.lead textarea{width:100%;min-height:50px;padding:0 14px;
  border:1.5px solid var(--line-2);border-radius:var(--r-input);background:#fff;
  color:var(--ink);font:500 15.5px/1 var(--font);text-transform:none;
  transition:border-color .15s,box-shadow .15s}
.lead textarea{min-height:82px;padding:12px 14px;line-height:1.5;resize:vertical}
.lead select{appearance:none;-webkit-appearance:none;cursor:pointer;padding-right:38px;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%234A6478' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 14px center;background-size:10px 6px}
.lead input::placeholder,.lead textarea::placeholder{color:var(--ink-3);font-weight:400}
.lead input:focus,.lead select:focus,.lead textarea:focus{outline:none;
  border-color:var(--ice-600);box-shadow:0 0 0 4px rgba(46,138,128,.15)}
.lead input[aria-invalid=true],.lead select[aria-invalid=true]{border-color:var(--stop);
  box-shadow:0 0 0 4px rgba(201,59,59,.13)}
/* The line under a field that is wrong, written by main.js. Every form uses
   it: the survey form, the price calculator's booking box and the two
   step-by-step calculators. It says what is wrong and how to put it right,
   and it goes the moment the field is right. */
.fld-err{display:flex;align-items:flex-start;gap:6px;margin-top:6px;font-size:13px;
  line-height:1.35;font-weight:600;color:var(--stop);text-align:left;
  animation:fldErr .22s ease both}
.fld-err::before{content:"!";flex:none;display:grid;place-items:center;width:16px;height:16px;
  margin-top:1px;border-radius:50%;background:currentColor;color:#fff;font-size:11px;font-weight:800;line-height:1}
.fld-err[hidden]{display:none}
@keyframes fldErr{from{opacity:0;translate:0 -3px}to{opacity:1;translate:0 0}}
.qz-field input[aria-invalid=true]{border-color:var(--stop);box-shadow:0 0 0 4px rgba(201,59,59,.13)}
.pcfg-book input[aria-invalid=true]{border-color:#FF8F7E;box-shadow:0 0 0 3px rgba(255,143,126,.22)}
.pcfg-out .fld-err{color:#FFB4A8}
.pcfg-out .fld-err::before{color:#3A0F09}
@media (prefers-reduced-motion:reduce){ .fld-err{animation:none} }
.lead-foot{font-size:12.5px;color:var(--ink-3);line-height:1.5}
/* THE OPTION BUTTON.

   The sister site's `.opt` is the reference and David asked for it by
   name: a 2px border, a real radius, proper padding, and two things ours never
   had. It lifts on hover, and it presses. `transform: scale(.985)` on :active
   is the whole difference between a control that feels like a button and one
   that feels like a div with a border, and it costs one line.

   Used by the survey form's "when" question. The price configurator's chips
   inherit the same treatment further down. */
.opts{display:grid;gap:9px}
.opts--2{grid-template-columns:1fr 1fr}
/* The service cards on the survey form are one line each, so they are
   shorter than a card with a note under it. */
.lead-job .opt > span{min-height:48px;padding:9px 13px}
/* An odd option out at the end takes the whole row rather than half of it. */
.opts--2 > .opt:last-child:nth-child(odd){grid-column:1 / -1}
.opt{position:relative;display:block}
.opt input{position:absolute;inset:0;opacity:0;cursor:pointer;margin:0}
.opt > span{display:grid;gap:2px;align-content:center;min-height:58px;
  padding:11px 15px;cursor:pointer;
  border:2px solid var(--line-2);border-radius:16px;background:#fff;
  transition:border-color .18s,background .18s,transform .16s,box-shadow .2s}
.opt b{font-size:14.2px;font-weight:700;color:var(--ink);line-height:1.25}
/* --ink-3 on the tinted chip of a selected option is 4.43 to 1, just under
   AA for text this size. --ink-2 is 5.9. This note appears under every radio
   in every form and every calculator on the site, so it is worth the shade. */
.opt em{font-style:normal;font-size:12px;color:var(--ink-2);line-height:1.3}
.opt input:hover + span{border-color:var(--ice-400);background:var(--ice-50);
  transform:translateY(-2px);box-shadow:0 8px 18px -10px rgba(30,134,188,.4)}
.opt input:active + span{transform:scale(.985)}
.opt input:checked + span{border-color:var(--ice-600);background:var(--ice-50);
  box-shadow:0 8px 20px -12px rgba(30,134,188,.5)}
.opt input:checked + span b{color:var(--ice-800)}
.opt input:focus-visible + span{outline:3px solid var(--ice-500);outline-offset:2px}

/* Same feel on the price configurator's chips and the segmented controls, so
   there is one kind of choosing on this site rather than three. */
.pcfg-chip span,.seg span{border-width:2px;border-radius:16px;
  transition:border-color .18s,background .18s,transform .16s,box-shadow .2s}
.pcfg-chip input:hover + span,.seg input:hover + span{transform:translateY(-2px);
  box-shadow:0 8px 18px -10px rgba(30,134,188,.4)}
.pcfg-chip input:active + span,.seg input:active + span{transform:scale(.985)}

/* SEND IT ANOTHER WAY. A quiet row under the submit button: the form is the
   main path and these are the two alternatives, so they are small and they do
   not compete with it. */
.lead-alt{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding-top:14px;margin-top:2px;border-top:1px solid var(--line)}
.lead-alt-l{font-size:12.5px;font-weight:600;color:var(--ink-3)}
.lead-alt-b{display:flex;gap:8px;margin-left:auto}
.lead-alt .btn{min-height:40px;padding:0 16px;font-size:13.5px}
.lead-alt .btn--wa svg{width:15px;height:15px}
/* `.hero .btn--ghost` paints ghost buttons white for the dark hero ground, and
   at two classes it reaches straight into the white form panel sitting on that
   ground. The Email button was white text on white. The lead form is always on
   a light surface wherever it appears, so it states its own colours and, being
   later in the file at the same specificity, wins. */
.lead-alt .btn--ghost{color:var(--ice-700);border-color:var(--line-2)}
.lead-alt .btn--ghost:hover{background:var(--ice-50);border-color:var(--ice-500);
  color:var(--ice-700)}

/* THE HOURS LINE. Small, factual, and it appears wherever somebody is about to
   pick up the phone rather than only in the footer. The dot and the "Open now"
   are written by JavaScript from the same `site.hours` the schema uses; with
   JavaScript off the line reads correctly without them. */
.hours{display:flex;align-items:baseline;flex-wrap:wrap;gap:4px 8px;
  font-size:12.5px;line-height:1.45;color:var(--ink-3)}
.hours-now{display:inline-flex;align-items:center;gap:6px;font-weight:700;
  color:var(--ok)}
.hours-now::before{content:"";width:7px;height:7px;border-radius:50%;
  background:var(--ok);flex:none}
.hours-now[data-shut]{color:var(--ink-2)}
.hours-now[data-shut]::before{background:var(--ink-3)}
.hours--dark{color:var(--on-dark-3);margin-top:16px}
.hours--dark .hours-now{color:var(--ice-300)}
.hours--dark .hours-now::before{background:var(--ice-300)}
.hours--dark .hours-now[data-shut]{color:var(--on-dark-2)}
.hours--dark .hours-now[data-shut]::before{background:var(--on-dark-3)}


.lead-foot.is-err{color:var(--stop);font-weight:600}
.lead-foot.is-ok{color:var(--ok);font-weight:600}
.lead button[disabled]{opacity:.6;cursor:progress}
.lead--compact .lead-foot{font-size:12px}
.lead-done{display:grid;gap:10px;text-align:center;padding:8px 0}
.lead-done b{font-size:18px;font-family:var(--display);font-weight:800;letter-spacing:-.02em}
.lead-done p{font-size:14.5px;color:var(--ink-2)}

/* On a dark surface the form keeps its own light card, so nothing here needs an
   on-dark variant. What does need saying is the mobile collapse. */
@media (max-width:1080px){
  .ctacard--form{grid-template-columns:1fr}
  .tenyr{grid-template-columns:1fr}
  .thero-grid{grid-template-columns:1fr}
  .thero-form{max-width:520px}
  .thero-credit{text-align:left}
}
@media (max-width:720px){
  .wa-fab{width:52px;height:52px}
  .wa-fab svg{width:25px;height:25px}
  .thero{padding-top:calc(var(--nav-h) + 36px)}
  .thero h1{font-size:clamp(30px,8vw,40px);max-width:none}
  .thero-cta .btn{flex:1 1 100%}
  .lead-row{grid-template-columns:1fr}
  .thero-form{max-width:none}
}

/* ==========================================================================
   BREADCRUMBS
   ========================================================================== */
/* The nav is fixed, so whatever comes first inside main has to clear it. On
   every page except the homepage that is the breadcrumb bar. Getting this wrong
   hides the first line of the page underneath the capsule. */
/* White since 11 September 2026. David: the dark blue band behind the menu bar
   at the top of every page, make it white. It was ink, which put a dark strip
   above every white page and made the whole site read heavier than it is. The
   capsule itself stays dark, with the dark-ground logo, and sits solid on the
   white so it does not turn a muddy grey through its own transparency. */
.crumbs{background:#fff;color:var(--ink-3);font-size:13.5px;
  border-bottom:1px solid var(--line)}
body:has(main > .crumbs:first-child) .nav{background:rgba(15,26,36,.96);
  box-shadow:0 14px 34px -20px rgba(17,26,34,.55)}
main > .crumbs:first-child{padding-top:calc(var(--nav-h) + 18px)}
.crumbs + .phero{padding-top:clamp(30px,3.2vw,48px)}
.crumbs + .pxhero{padding-top:clamp(26px,3vw,44px)}
.crumbs + .band,.crumbs + .pcheck-sec{padding-top:clamp(40px,5vw,72px)}
/* OVER THE HERO, not above it. David, 11 September 2026: on the pages with a
   photograph or a clip behind the hero, the white breadcrumb strip is a bar
   between the menu and the picture. On those pages the crumbs sit on the
   picture instead, in white, and the hero starts under the navigation the way
   the home page does. The strip stays white everywhere else, where the page
   below it is white anyway. */
.thero > .crumbs,.phero > .crumbs,.lp-hero--video > .crumbs{
  position:absolute;top:0;left:0;right:0;z-index:3;
  background:none;border-bottom:0;padding-top:calc(var(--nav-h) + 10px);color:#D2E2EE}
.thero > .crumbs a,.phero > .crumbs a,.lp-hero--video > .crumbs a{color:#D2E2EE}
.thero > .crumbs a:hover,.phero > .crumbs a:hover,.lp-hero--video > .crumbs a:hover{color:#fff}
.thero > .crumbs span,.phero > .crumbs span,.lp-hero--video > .crumbs span{color:#fff}
/* One line on a phone. Wrapped, the last crumb landed on top of the headline
   ("Air conditioning Barnet" sitting on "Air conditioning in"), so the trail
   stays on one row and the current page's name is the part that gives way. */
@media (max-width:720px){
  :is(.thero,.phero,.lp-hero--video) > .crumbs ol{flex-wrap:nowrap;white-space:nowrap}
  :is(.thero,.phero,.lp-hero--video) > .crumbs li{flex:none}
  :is(.thero,.phero,.lp-hero--video) > .crumbs li:last-child{flex:1 1 auto;min-width:0;overflow:hidden}
  :is(.thero,.phero,.lp-hero--video) > .crumbs li:last-child span{display:block;overflow:hidden;text-overflow:ellipsis}
}
.thero:has(> .crumbs),.phero:has(> .crumbs),
.lp-hero--video:has(> .crumbs){padding-top:calc(var(--nav-h) + 74px)}
.crumbs ol{list-style:none;display:flex;flex-wrap:wrap;gap:4px 8px;padding:12px 0}
.crumbs li:not(:last-child)::after{content:"/";padding-left:8px;opacity:.45}
.crumbs a{color:var(--ink-2);text-decoration:none}
.crumbs a:hover{color:var(--ice-700);text-decoration:underline}
.crumbs span{color:var(--ink);font-weight:600}

/* Legal and policy page header. No photograph: a picture on a complaints
   procedure is noise. It just needs to stop looking like an unstyled document. */
.lhead{background:#fff;color:var(--ink);border-bottom:1px solid var(--line);padding-block:clamp(40px,5vw,72px) clamp(30px,3.4vw,48px);
  border-bottom:1px solid var(--line-dark)}
.lhead h1{color:var(--ink);font-size:clamp(30px,3.4vw,46px)}
.lhead .lede{color:var(--ink-2);margin-top:12px}
main > .crumbs:first-child + .lhead{padding-top:clamp(30px,3.4vw,52px)}

.faq-head{max-width:56ch}
.faq-head .lede{color:#D2E2EE;margin-top:16px}

/* ==========================================================================
   CONTENT
   ========================================================================== */
.answer{background:var(--ice-50);border-left:3px solid var(--ice-600);
  border-radius:0 var(--r-panel) var(--r-panel) 0;
  padding:clamp(22px,2.2vw,28px) clamp(22px,2.4vw,32px);display:grid;gap:12px;max-width:78ch}
.answer-t{font-size:clamp(17px,.55vw + 16px,20px);font-weight:600;color:var(--ink);line-height:1.5}
.answer-x{font-size:15.5px;color:var(--ink-2)}
.answer a{color:var(--ice-700);font-weight:600}

.prose{max-width:72ch;display:grid;gap:20px}
.prose h2{margin-top:24px}
.prose h3{margin-top:12px;font-size:clamp(18px,1.2vw,21px)}
.prose p,.prose li{font-size:16.5px;color:var(--ink-2);line-height:1.68}
.prose strong{color:var(--ink);font-weight:600}
.prose a{color:var(--ice-700);font-weight:500}
.prose ul,.prose ol{display:grid;gap:10px;padding-left:22px}
.prose li{padding-left:4px}
.prose ul li{list-style:disc}
.prose ol li{list-style:decimal}
.prose--wide{max-width:none}

.split{display:grid;grid-template-columns:minmax(0,1.18fr) minmax(0,.82fr);
  gap:var(--gap);align-items:start}
.sticky-side{position:sticky;top:calc(var(--nav-h) + 26px);display:grid;gap:18px}

/* ==========================================================================
   TABLES
   ========================================================================== */
.tbl-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r-panel);background:#fff}
.dtable{width:100%;font-size:15px;min-width:min(560px,100%)}
.dtable caption{text-align:left;font-size:14px;font-weight:600;color:var(--ink);
  padding:18px 20px 0;caption-side:top}
.dtable th,.dtable td{text-align:left;padding:14px 20px;border-bottom:1px solid var(--line);
  vertical-align:top}
.dtable thead th{font-size:12.5px;font-weight:700;color:var(--ink);background:var(--paper-50);
  border-bottom:1px solid var(--line-2)}
.dtable tbody th{font-weight:600;color:var(--ink)}
.dtable td{color:var(--ink-2)}
.dtable tbody tr:last-child th,.dtable tbody tr:last-child td{border-bottom:0}
.yes{color:var(--ok);font-weight:700}
.no{color:var(--stop);font-weight:700}
/* --hold is #A9701A, which is 4.18 to 1 on white at 15px bold and just under
   AA. This is a verdict on a planning page, so it is exactly the text somebody
   squints at. Two shades down clears it at 5.6. */
.maybe{color:#8A5B12;font-weight:700}
.tbl-note{font-size:13.5px;color:var(--ink-3);margin-top:12px}

/* ==========================================================================
   CARDS AND GRIDS
   ========================================================================== */
.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.cards--2{grid-template-columns:repeat(2,1fr)}
.cards--4{grid-template-columns:repeat(4,1fr)}
.card{background:#fff;border:1px solid var(--line);border-radius:var(--r-panel);
  padding:clamp(22px,2vw,28px);display:grid;gap:11px;align-content:start;
  transition:transform .2s ease,box-shadow .2s ease,border-color .2s}
a.card{text-decoration:none}
a.card:hover{transform:translateY(-3px);box-shadow:var(--sh);border-color:var(--ice-400)}
.card h3{font-size:var(--h-card)}
.card p{font-size:15px;color:var(--ink-2)}
.card .more{margin-top:auto;font-weight:700;font-size:14.5px;color:var(--ice-700);
  display:inline-flex;align-items:center;gap:7px}
.card .more::after{content:"";width:6px;height:6px;border-right:2px solid currentColor;
  border-top:2px solid currentColor;rotate:45deg;transition:translate .16s}
a.card:hover .more::after{translate:3px 0}
.card-n{font-family:var(--display);font-size:34px;font-weight:800;color:var(--ice-700);
  letter-spacing:-.04em;line-height:1}
.card--warm{border-color:var(--warm-100);background:var(--warm-50)}
.card--warm .card-n,.card--warm .more{color:var(--warm-ink)}
.card--warm .ic-lg{color:var(--warm)}

.ic-lg{width:32px;height:32px;color:var(--ice-600);stroke:currentColor;fill:none;
  stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}

/* Feature rail: a photograph with the answer under it. Not an icon in a circle. */
.qcards{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.qcard{background:#fff;border:1px solid var(--line);border-radius:var(--r-panel);
  display:flex;flex-direction:column;overflow:hidden;
  transition:transform .2s ease,box-shadow .2s ease}
.qcard:hover{transform:translateY(-3px);box-shadow:var(--sh)}
.qcard img{aspect-ratio:5/4;object-fit:cover;width:100%}
.qcard-b{padding:24px;display:grid;gap:11px;align-content:start;flex:1}
.qcard p{font-size:15px;color:var(--ink-2)}
.qcard a{margin-top:auto;font-weight:700;font-size:14.5px;text-decoration:none;
  color:var(--ice-700);display:inline-flex;align-items:center;gap:7px;
  min-height:44px;width:max-content}
.qcard a::after{content:"";width:6px;height:6px;border-right:2px solid currentColor;
  border-top:2px solid currentColor;rotate:45deg;transition:translate .16s}
.qcard a:hover::after{translate:3px 0}

/* ==========================================================================
   PROCESS
   ========================================================================== */
.steps{display:grid;grid-template-columns:repeat(4,1fr);counter-reset:s;
  border:1px solid var(--line);border-radius:var(--r-panel);overflow:hidden;background:#fff}
.steps li{counter-increment:s;list-style:none;padding:clamp(24px,2.2vw,30px);
  display:grid;gap:9px;align-content:start}
.steps li + li{border-left:1px solid var(--line)}
.steps li::before{content:counter(s);font-family:var(--display);font-size:26px;
  font-weight:800;color:var(--ice-400);line-height:1}
.steps h3{font-size:var(--h-card)}
.steps p{font-size:14.5px;color:var(--ink-2)}

/* ==========================================================================
   RECENT INSTALLS RAIL
   ========================================================================== */
.rail{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(258px,1fr);gap:18px;
  overflow-x:auto;padding:4px 4px 20px;scroll-snap-type:x mandatory;overscroll-behavior-x:contain}
.rail::-webkit-scrollbar{height:8px}
.rail::-webkit-scrollbar-track{background:var(--paper-200);border-radius:99px}
.rail::-webkit-scrollbar-thumb{background:var(--ice-500);border-radius:99px}
.job{scroll-snap-align:start;background:var(--ink-850);border:1px solid var(--line-dark);
  border-radius:var(--r-panel);overflow:hidden}
.job img{aspect-ratio:4/5;object-fit:cover;width:100%}
.job-b{padding:18px;display:grid;gap:11px}
.job-t{font-size:15.5px;font-weight:700;color:#fff;font-family:var(--display);letter-spacing:-.02em}
.job dl{display:grid;grid-template-columns:auto minmax(0,1fr);gap:6px 14px;font-size:13.5px;margin:0}
.job dt{color:var(--on-dark-3)}
.job dd{margin:0;text-align:right;font-weight:600;color:var(--on-dark);text-wrap:balance}

/* ==========================================================================
   SYSTEMS
   ========================================================================== */
.sys{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.sys figure{margin:0;background:#fff;border:1px solid var(--line);
  border-radius:var(--r-panel);overflow:hidden}
.sys img{aspect-ratio:4/5;object-fit:cover;width:100%}
.sys figcaption{padding:22px;display:grid;gap:9px}
.sys p{font-size:14.5px;color:var(--ink-2)}
.kw{display:inline-flex;align-items:center;font-size:12.8px;font-weight:700;
  color:var(--ice-700);background:var(--ice-50);border-radius:var(--r-pill);
  padding:6px 13px;width:max-content}


/* ==========================================================================
   PRICE CONFIGURATOR

   One block, not a wall of boxes. It used to lay eight bordered white cards out
   in two columns, all open, which David called redundant and was right about:
   eight questions on screen at once is a form, and nobody fills in a form to
   find out a price. It is a stepper at every width now, two questions to a step
   on a desktop and one on a phone, inside a single gradient card with the price
   beside it.

   The two-per-step rule is in the note on `wide` in main.js: above one question
   per step, choosing an answer does not advance, because it would take the
   question next to it away before it had been read.
   ========================================================================== */

/* ==========================================================================
   THE PER-PAGE ESTIMATOR
   The eight-question configurator is the right tool on the home page and the
   cost page and the wrong one everywhere else: somebody who has just read a
   page about servicing does not want to be asked about pipe runs and outdoor
   positions. This is the small one. One to three questions the page can
   actually ask, the same model behind it, and it sits in the flow of the page
   rather than owning a band of its own.
   ========================================================================== */
/* Half the usual band padding: the card is already a box with its own inset,
   and a full band around a box reads as two nested boxes. */
.band--est{padding-block:calc(var(--band) * .5)}
/* STEPS, David's brief of 11 September 2026. A laptop gets a photograph on
   the left with the price sitting on it, and one question at a time on the
   right; the last step is the form. The card is as tall as one question, not
   as tall as all of them plus a form. */
.pmini{display:grid;grid-template-columns:minmax(0,380px) minmax(0,1fr);
  border:1px solid var(--line);border-radius:var(--r-panel);background:#fff;overflow:hidden;
  box-shadow:var(--sh-sm)}
.pmini-media{position:relative;min-height:420px;background:var(--paper-50)}
.pmini-media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.pmini-out{position:absolute;left:16px;right:16px;bottom:16px;padding:16px 16px 14px;
  background:#fff;border-radius:16px;box-shadow:0 18px 40px -18px rgba(17,26,34,.45)}
.pmini-kicker{margin:0;font-size:11.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ice-700)}
.pmini-money{display:flex;flex-wrap:wrap;align-items:baseline;gap:9px;margin-top:4px;
  font-family:var(--display);font-weight:800;letter-spacing:-.025em;color:var(--ink);
  font-size:clamp(24px,2.3vw,30px);line-height:1.1}
.pmini-money i{font-style:normal;font-size:13px;font-weight:600;color:var(--ink-3)}
.pmini-unit{margin-top:5px;font-size:12.5px;color:var(--ink-2);line-height:1.45}
.pmini-picks{margin-top:8px;font-size:12.5px;font-weight:600;color:var(--ink);line-height:1.45}
.pmini-wa{width:100%;margin-top:12px}
.pmini-main{display:flex;flex-direction:column;gap:14px;min-width:0;
  padding:clamp(20px,2.2vw,28px) clamp(18px,2.4vw,32px)}
.pmini-hd h2{font-size:var(--h-panel);color:var(--ink)}
.pmini-hd p{margin-top:6px;font-size:14px;color:var(--ink-2);line-height:1.55;max-width:60ch}
.pmini-prog{display:flex;align-items:center;gap:12px}
.pmini-prog-t{flex:none;font-size:12.5px;font-weight:700;color:var(--ink-2)}
.pmini-prog-bar{flex:1;height:6px;border-radius:99px;background:var(--line);overflow:hidden}
.pmini-prog-bar i{display:block;height:100%;border-radius:inherit;background:var(--ice-600);
  transition:width .35s cubic-bezier(.2,.7,.2,1)}
.pmini-steps{display:grid;gap:18px}
.pmini-q{border:0;padding:0;margin:0;min-width:0}
.pmini-q legend,.pmini-send-t{font-size:16px;font-weight:750;color:var(--ink);padding:0;margin:0 0 10px;outline:0}
/* One step at a time once the script is running. Without it every step shows. */
.pmini.is-live .pm-step:not(.is-on){display:none}
@media (prefers-reduced-motion:no-preference){
  .pmini.is-live .pm-step.is-on{animation:pmIn .32s cubic-bezier(.2,.7,.2,1) both}
  .pmini.is-live.is-back .pm-step.is-on{animation-name:pmBack}
}
@keyframes pmIn{from{opacity:0;transform:translateX(18px)}to{opacity:1;transform:none}}
@keyframes pmBack{from{opacity:0;transform:translateX(-18px)}to{opacity:1;transform:none}}
.pmini-nav{display:flex;align-items:center;justify-content:flex-end;gap:12px;margin-top:auto}
.pmini-back{margin-right:auto;min-height:44px;padding:0 6px;border:0;background:none;cursor:pointer;
  font:inherit;font-size:14px;font-weight:650;color:var(--ice-700);text-decoration:underline;text-underline-offset:3px}
.pmini-back:hover{color:var(--ice-800)}
.pmini-note{margin:0;font-size:11.5px;color:var(--ink-3);line-height:1.5}
/* The last step, the form. */
.pmini-send{display:grid;gap:10px;min-width:0}
.pmini-send-t{margin-bottom:0}
.pmini-fields{display:grid;grid-template-columns:1fr 1fr;gap:10px 12px}
.pm-fld{display:grid;gap:3px;min-width:0}
.pm-fld--wide{grid-column:1 / -1}
.pm-fld span{font-size:12.5px;font-weight:650;color:var(--ink-2)}
.pm-fld input{min-height:44px;padding:0 12px;border:1.5px solid var(--line-2);border-radius:var(--r-input);
  font:inherit;font-size:16px;color:var(--ink);background:#fff;min-width:0;width:100%}
.pm-fld input:focus{outline:0;border-color:var(--ice-600);box-shadow:0 0 0 3px rgba(58,163,216,.2)}
.pm-fld input[aria-invalid=true]{border-color:var(--stop);box-shadow:0 0 0 3px rgba(201,59,59,.13)}
.pmini-send-msg{margin:0;font-size:13px;color:var(--ink-2)}
.pmini-send-msg.is-err{color:var(--stop);font-weight:650}
/* A phone: no photograph, the price as a slim bar across the top of the card,
   then the one question. Shorter than the old card by two thirds. */
@media (max-width:860px){
  .pmini{grid-template-columns:minmax(0,1fr)}
  .pmini-media{min-height:0;background:none;order:1;padding:0 16px}
  .pmini-media img{display:none}
  .pmini-out{position:static;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;
    column-gap:12px;padding:12px 14px;border:1px solid var(--line);border-radius:14px;
    background:var(--paper-50);box-shadow:none}
  .pmini-kicker,.pmini-unit,.pmini-picks{grid-column:1}
  .pmini-money{grid-column:1;font-size:24px;margin-top:2px}
  .pmini-unit{margin-top:2px;font-size:12px}
  .pmini-picks{display:none}
  .pmini-wa{grid-column:2;grid-row:1 / span 3;width:auto;margin:0;padding:0 14px;min-height:44px}
  .pmini-wa span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
  .pmini-main{display:contents}
  .pmini-hd{order:0;padding:20px 16px 12px}
  .pmini-prog{order:2;padding:14px 16px 0}
  .pmini-steps{order:3;padding:12px 16px 0}
  .pmini-nav{order:4;padding:12px 16px 0}
  .pmini-note{order:5;padding:12px 16px 18px}
}
@media (max-width:420px){
  .pmini-fields{grid-template-columns:minmax(0,1fr)}
}

.pcfg{position:relative;isolation:isolate;overflow:hidden;
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,364px);
  gap:clamp(18px,2vw,28px);align-items:start;
  border-radius:clamp(20px,2.2vw,28px);padding:clamp(18px,2vw,28px);
  background:linear-gradient(150deg,var(--ink-900) 0%,var(--ice-900) 58%,var(--ice-800) 100%);
  box-shadow:0 30px 70px -34px rgba(17,83,111,.6)}
/* ONE PANEL, several questions. It used to be two separate white cards side by
   side inside the gradient, and David's note is exactly right: two short cards
   in a wide card leaves a band of empty gradient under them and the whole thing
   reads as unfinished. One panel holding three questions stacked is the same
   information at the same size as the price panel beside it, so the card has
   two columns of roughly equal height and no dead space.

   The questions are rows in that panel now, divided by a hairline, rather than
   eight bordered boxes. */
.pcfg-qs{order:1;align-content:start;background:rgba(255,255,255,.97);
  border-radius:18px;overflow:hidden;box-shadow:0 14px 34px -20px rgba(6,20,28,.55);
  display:grid;grid-template-columns:minmax(0,1fr);gap:0}
.pcfg-out{order:2;background:rgba(8,18,28,.55);color:var(--on-dark);
  border:1px solid rgba(216,232,244,.14);border-radius:var(--r-panel);
  padding:clamp(20px,1.9vw,26px);display:grid;gap:13px;
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}

.pcfg-figure{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
.pcfg-money{font-family:var(--display);font-weight:800;letter-spacing:-.045em;
  line-height:1;color:#fff;font-size:clamp(30px,3vw,42px);
  font-variant-numeric:tabular-nums lining-nums}
.pcfg-to{color:var(--on-dark-3);font-size:15px}
.pcfg-vat{font-size:13.5px;color:var(--on-dark-2)}

.pcfg-bd{border-top:1px solid var(--line-dark);padding-top:12px}
.pcfg-bd-t{font-size:11.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--on-dark-3);margin-bottom:4px}
.pcfg-bd .bd{background:transparent;font-size:13.5px}
.pcfg-bd .bd td{border-bottom:1px solid var(--line-dark);color:var(--on-dark-2);padding:9px 0}
.pcfg-bd .bd td:last-child{text-align:right;color:#fff;font-weight:600;white-space:nowrap}
.pcfg-bd .bd tr:last-child td{border-bottom:0}
.pcfg-cta{display:grid;gap:9px}
.pcfg-out .btn--ghost{color:#fff;border-color:rgba(255,255,255,.34)}
.pcfg-out .btn--ghost:hover{background:rgba(255,255,255,.1);border-color:#fff}
.pcfg-note{font-size:12.5px;color:var(--on-dark-3);line-height:1.5}

.pcfg-q{background:none;border:0;border-radius:0;display:grid;align-content:start;
  box-shadow:none;border-bottom:1px solid var(--line)}
.pcfg-q:last-child,.pcfg-q.is-last{border-bottom:0}
/* The panel owns the rounding, so the first and last visible rows must not
   fight it. `:has` covers the stepped case where "first" changes per step. */
[data-stepped="on"] .pcfg-q.is-current:last-of-type{border-bottom:0}

.pcfg-row{display:flex;align-items:center;justify-content:space-between;gap:12px;
  width:100%;padding:16px 18px;background:none;border:0;cursor:pointer;text-align:left;
  font:inherit;color:inherit}
.pcfg-row-l{font-size:14.5px;font-weight:700;color:var(--ink)}
.pcfg-row-v{font-size:13.5px;font-weight:600;color:var(--ice-700);text-align:right}
.pcfg-opts{padding:0 18px 18px;display:grid;gap:12px}
.pcfg-help{font-size:13px;color:var(--ink-2);line-height:1.5}
.pcfg-chips{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.pcfg-chip{position:relative;display:block}
.pcfg-chip input{position:absolute;inset:0;opacity:0;cursor:pointer;margin:0}
.pcfg-chip span{display:grid;gap:2px;min-height:52px;padding:9px 13px;align-content:center;
  border:1.5px solid var(--line-2);border-radius:var(--r-input);background:#fff;cursor:pointer;
  transition:border-color .14s,background .14s}
.pcfg-chip b{font-size:13.8px;font-weight:600;color:var(--ink);line-height:1.25}
/* Same shade correction as .opt em. --ink-3 on the ice-50 fill of a selected
   chip is 4.43 to 1 and this is 11.8px, so it needs 4.5. */
.pcfg-chip em{font-style:normal;font-size:11.8px;color:var(--ink-2);line-height:1.3}
.pcfg-chip input:hover + span{border-color:var(--ice-400)}
.pcfg-chip input:checked + span{border-color:var(--ice-600);background:var(--ice-50)}
.pcfg-chip input:checked + span b{color:var(--ice-700);font-weight:700}
.pcfg-chip input:focus-visible + span{outline:3px solid var(--ice-500);outline-offset:2px}
/* A checkbox needs to look different from a radio, or nobody realises they can
   pick more than one. */
.pcfg-chips--check .pcfg-chip span{padding-left:40px}
.pcfg-chips--check .pcfg-chip span::before{content:"";position:absolute;left:13px;top:50%;
  translate:0 -50%;width:18px;height:18px;border:1.5px solid var(--line-2);border-radius:5px;
  background:#fff;transition:background .14s,border-color .14s}
.pcfg-chips--check .pcfg-chip span::after{content:"";position:absolute;left:18px;top:50%;
  width:8px;height:4px;border-left:2px solid #fff;border-bottom:2px solid #fff;
  rotate:-45deg;translate:0 -70%;opacity:0;transition:opacity .14s}
.pcfg-chips--check .pcfg-chip input:checked + span::before{background:var(--ice-600);
  border-color:var(--ice-600)}
.pcfg-chips--check .pcfg-chip input:checked + span::after{opacity:1}

/* The stepped state, at every width now rather than only on a phone: the
   configurator opts in with data-step-size and the other calculators still only
   step below 720. See memory/forms-are-paged-on-mobile.md and the note on the
   stepper in main.js. */
[data-stepped="on"] [data-step]{display:none}
[data-stepped="on"] [data-step].is-current{display:grid}
[data-stepped="on"] .pcfg-q{display:none}
[data-stepped="on"] .pcfg-q.is-current{display:grid}
/* While stepping, the row header is the step title rather than a control. */
[data-stepped="on"] .pcfg-row{padding-bottom:0;pointer-events:none}
[data-stepped="on"] .pcfg-row::after{content:none}
[data-stepped="on"] .pcfg-row-v{display:none}
/* The extras question spans both columns when everything is shown; stepped, it
   is a step like any other. */
[data-stepped="on"] .pcfg-q--extras{grid-column:auto}

/* The stepper furniture sits on the gradient, so it inverts. */
/* The panel's own Next, and the line under it. It sits where the three send
   buttons used to, so the panel is never a dead box of numbers. */
.pcfg-next{display:grid;gap:8px;margin-top:14px}
.pcfg-next[hidden]{display:none}
.pcfg-cta[hidden]{display:none}
.pcfg-left{font-size:12.5px;color:var(--on-dark-2);text-align:center}

/* THE BOOKING BLOCK, on the last step only. The panel is dark, so the fields
   are the inverse of the ones in the survey form: a light face on a dark card
   rather than a dark face on a white one. */
.pcfg-or{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:10px;
  margin:14px 0 12px}
.pcfg-or::before,.pcfg-or::after{content:"";height:1px;background:rgba(216,232,244,.22)}
.pcfg-or span{font-size:11.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--on-dark-3)}
.pcfg-book{display:grid;gap:9px}
.pcfg-book-row{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.pcfg-book .fld{display:grid;gap:5px;min-width:0}
.pcfg-book .fld > span{font-size:12px;font-weight:600;color:var(--on-dark-2)}
.pcfg-book .fld > span i{font-style:normal;font-weight:500;color:var(--on-dark-3)}
.pcfg-book input{width:100%;min-height:42px;padding:0 12px;border-radius:var(--r-input);
  border:1.5px solid rgba(216,232,244,.28);background:rgba(255,255,255,.07);color:#fff;
  font:500 14.5px var(--font)}
.pcfg-book input::placeholder{color:rgba(216,232,244,.5)}
.pcfg-book input:focus{outline:0;border-color:var(--ice-300);background:rgba(255,255,255,.12)}
.pcfg-book-msg{font-size:12.5px;color:var(--on-dark-2);line-height:1.5}
.pcfg-book-msg.is-err{color:#FFB4A8;font-weight:600;cursor:pointer}
.pcfg-book .lead-done{color:#fff;font-size:14px;line-height:1.55}
.pcfg-book .lead-done p{margin-top:6px;color:var(--on-dark-2)}
@media (max-width:520px){
  .pcfg-book-row{grid-template-columns:1fr}
}

.pcfg .stepnav{grid-column:1/-1;order:3;padding:2px 2px 0}
.pcfg .stepnav-bar{background:rgba(255,255,255,.18)}
.pcfg .stepnav-bar i{background:var(--ice-300)}
.pcfg .stepnav-n{color:var(--on-dark-2)}
.pcfg .stepbtn{background:rgba(255,255,255,.1);border-color:rgba(216,232,244,.28);color:#fff}
.pcfg .stepbtn:hover:not(:disabled){border-color:#fff;color:#fff}
.pcfg .stepbtn--next,.pcfg .stepbtn--next:hover{background:var(--ice-300);
  border-color:var(--ice-300);color:var(--ink)}

/* Bigger and brighter from the tablet width up.

   On a phone the stepper is the only thing under the question and nobody
   misses it. On a desktop it sits at the bottom of a 1200px card, on a dark
   gradient, and Back was white text at 10 per cent background behind a 28 per
   cent border, dropping to 42 per cent opacity while disabled on step one,
   which is where anyone opening the calculator starts. David could not see
   the controls, which on a three step form means he could not see that there
   were three steps.

   Nothing here changes behaviour: same buttons, same order, more contrast and
   a larger target. */
@media (min-width:721px){
  .pcfg .stepnav{gap:16px;padding-top:8px}
  .pcfg .stepnav-bar{height:6px;background:rgba(255,255,255,.24)}
  .pcfg .stepnav-n{color:#fff;font-size:14px}
  .pcfg .stepbtn{min-height:50px;padding:0 30px;font-size:15px;
    background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.55);color:#fff}
  .pcfg .stepbtn:hover:not(:disabled){background:rgba(255,255,255,.26);
    border-color:#fff;color:#fff}
  /* Still obviously unavailable, but readable rather than a smudge. */
  .pcfg .stepbtn:disabled{opacity:.6}
  .pcfg .stepbtn--next,.pcfg .stepbtn--next:hover{padding:0 38px;
    box-shadow:0 12px 26px -12px rgba(6,20,30,.75)}
}


/* The chevron only exists where the rows actually collapse. */
.pcfg-row::after{content:none}

@media (max-width:1080px){
  .pcfg{grid-template-columns:1fr}
  .pcfg-out{order:0;position:sticky;top:calc(var(--nav-h) + 14px);z-index:5}
  .pcfg-qs{order:1}
}
@media (max-width:720px){
  .pcfg-qs{grid-template-columns:1fr;gap:10px}
  /* Compact price bar: one line, no breakdown open, nothing else competing. */
  .pcfg-out{padding:14px 16px;gap:10px;border-radius:var(--r-input);
    box-shadow:0 14px 34px -18px rgba(10,17,25,.7)}
  .pcfg-money{font-size:26px}
  .pcfg-vat{display:none}
  .pcfg-note{display:none}
  .pcfg-cta{grid-template-columns:1fr}
  .pcfg-out .btn{min-height:48px;font-size:14.5px}
  /* Dead accordion, removed 2026-09-05. `.pcfg-q:not(.is-open) .pcfg-opts
     { display: none }` outlived the JS that set `.is-open`: when the stepper
     replaced the accordion nothing added that class any more, so on a phone
     every question showed its title and none of its answers. The stepper shows
     one question at a time and shows it open, and with JS off all eight are
     open, which is the right fallback. */
  .pcfg-row{padding:15px 16px 4px}
  .pcfg-chips{grid-template-columns:1fr}
  .pcfg-chip span{min-height:50px}
}

/* ==========================================================================
   SERVICES BENTO
   The layout Sentry uses, adapted. Six services, six cells, no empty tiles:
   two columns of weight and two of support, so nothing becomes a letterbox.
   Every card is a real photograph of real work with a scrim and the text at
   the bottom, which is the whole reason it beats six icons in circles.
   ========================================================================== */
/* The three wrappers around the services grid are inert at every width:
   `display: contents` removes them from the layout entirely, so the cards are
   direct children of `.svc` and the grid works as if the wrappers were not
   there. They exist because this markup was a belt on a phone for a day, and
   the bento replaced it. */
.svc-belt{display:contents}
.svc-view{display:contents}
.svc-group{display:contents}

.svc{display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:14.5rem;gap:14px}
.svc-card{position:relative;display:flex;align-items:flex-end;overflow:hidden;
  border-radius:var(--r-panel);text-decoration:none;background:var(--ink-850);isolation:isolate}
.svc-card--feature{grid-column:span 2;grid-row:span 2}
.svc-card--wide{grid-column:span 2;grid-row:span 2}
.svc-card--tall{grid-column:span 1;grid-row:span 2}
.svc-media{position:absolute;inset:0;z-index:0}
.svc-media img{width:100%;height:100%;object-fit:cover;transform:scale(1.02);
  filter:brightness(.86);transition:transform .9s cubic-bezier(.16,1,.3,1),filter .3s ease}
.svc-card:hover .svc-media img{transform:scale(1.07);filter:brightness(1)}
.svc-card::before{content:"";position:absolute;inset:0;z-index:1;
  background:linear-gradient(180deg,rgba(10,17,25,0) 0%,rgba(10,17,25,.2) 34%,rgba(10,17,25,.78) 72%,rgba(10,17,25,.96) 100%)}
.svc-card::after{content:"";position:absolute;inset:0;z-index:3;border-radius:inherit;
  border:1px solid rgba(216,232,244,.16);pointer-events:none;transition:border-color .2s}
.svc-card:hover::after{border-color:var(--ice-400)}
.svc-body{position:relative;z-index:2;padding:clamp(18px,1.7vw,24px);width:100%}
.svc-price{display:block;font-size:12.5px;font-weight:700;color:var(--ice-300);margin-bottom:6px}
.svc-card--warm .svc-price{color:#F7A484}
.svc-name{font-family:var(--display);font-weight:800;letter-spacing:-.025em;
  color:#fff;font-size:19px;line-height:1.12}
.svc-card--feature .svc-name,.svc-card--wide .svc-name{font-size:clamp(21px,1.8vw,26px)}
.svc-sum{margin-top:8px;font-size:14.5px;line-height:1.5;color:rgba(231,238,244,.85);max-width:40ch}
/* Only the two big cards have room for a sentence. The rest stay quiet. */
.svc-card--tall .svc-sum{display:none}
.svc-cue{display:inline-flex;align-items:center;gap:7px;margin-top:12px;
  font-size:13.5px;font-weight:700;color:#fff}
.svc-cue::after{content:"";width:6px;height:6px;border-right:2px solid currentColor;
  border-top:2px solid currentColor;rotate:45deg;transition:translate .16s}
.svc-card:hover .svc-cue::after{translate:3px 0}

/* Integrated systems. Two cards, not a grid of six: there are exactly two of
   these products and a grid with four empty cells in it is a planning error. */
.intg{display:grid;gap:14px}
.intg-card{background:var(--ink-850);border:1px solid var(--line-dark);
  border-radius:var(--r-panel);padding:clamp(20px,2vw,26px);display:grid;gap:9px}
.intg-card h3{color:#fff;font-size:18px}
.intg-brand{font-size:13px;font-weight:700;color:var(--ice-300)}

/* Pictures in the integrated band. The cards were text on a dark ground; they
   are now a photograph with the text under it, and the copy column carries a
   third. Everything sits on --ink-850 rather than a border, so the cards read
   as objects on the band rather than as boxes drawn on it. */
.intg-card{overflow:hidden;padding:0;background:var(--ink-850);border:0}
.intg-media{aspect-ratio:16/9;overflow:hidden;background:var(--ink-800)}
.intg-media img{width:100%;height:100%;object-fit:cover;display:block}
.intg-body{padding:clamp(16px,1.6vw,22px)}
.intg-body h3{margin-bottom:4px}
/* The heading across the top, the sentence and the buttons beside it, and the
   two cards side by side underneath at the same height. */
.intg-head{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,1fr);
  gap:var(--gap);align-items:end;margin-bottom:clamp(24px,3vw,40px)}
.intg-head .lede{margin:0}
.intg-head .hero-cta{margin-top:18px}
.intg{grid-template-columns:repeat(2,minmax(0,1fr));gap:20px;align-items:stretch}
.intg .intg-media{aspect-ratio:2/1}
@media (max-width:860px){
  .intg-head{grid-template-columns:minmax(0,1fr);gap:14px;align-items:start}
  .intg{grid-template-columns:minmax(0,1fr);gap:14px}
}

.intg-card p:last-child{font-size:14.8px;color:var(--on-dark-2);line-height:1.6}

/* ==========================================================================
   COVERAGE MAP
   Real London borough boundaries, generated at build time from Office for
   National Statistics data. No tile service and no third party request, so the
   map costs the visitor nothing and cannot leak them to anyone.
   ========================================================================== */
.cov{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,420px);
  gap:var(--gap);align-items:center}
.cov-map{position:relative}
.cov-map svg{width:100%;height:auto;display:block;overflow:visible}
/* EVERY BOROUGH IS FILLED, and that is the point of this rule.

   The uncovered ones used to be white at 5%, which is all but invisible on the
   dark band, so the map said "we work in eleven boroughs and those grey shapes
   are somewhere else". We cover all thirty three. The difference between the
   two fills is now the difference between "most of our work is here" and "also
   here, same price", which is what it is, rather than the difference between
   coloured and absent. */
.cov-b{fill:var(--ice-50);stroke:var(--ice-300);stroke-width:.9;
  transition:fill .18s ease,stroke .18s ease}
/* The main area, north of the Thames: the strong blue, white edges between
   boroughs, and the name on each. A borough with a page is a link and goes
   darker under the pointer. */
.cov-b--main{fill:var(--ice-600);stroke:#fff;stroke-width:1.4}
a .cov-b--main{cursor:pointer}
a:hover .cov-b--main,a:focus-visible .cov-b--main{fill:var(--ice-800)}
.cov-map .cov-label.cov-label--main{fill:#fff;stroke:var(--ice-900);stroke-width:3.4px;font-weight:700;font-size:20px}
.cov-map .cov-label.cov-label--sm{font-size:15px;stroke-width:2.8px}
.cov-map .cov-label.cov-label--xs{font-size:10.5px;stroke-width:2.2px}
/* Twenty-one names as chips rather than a two-column table of rows. */
.cov .cov-list{display:flex;flex-wrap:wrap;gap:7px}
.cov .cov-list a,.cov .cov-list span{display:inline-block;padding:7px 12px;border-radius:var(--r-pill);font-size:13.5px}
.cov-label{font:600 11px var(--font);fill:var(--ink);paint-order:stroke;
  stroke:rgba(255,255,255,.9);stroke-width:3px;stroke-linejoin:round;pointer-events:none}
.cov-base{r:5;fill:var(--warm);stroke:#fff;stroke-width:2}
.cov-base-l{font:700 11.5px var(--font);fill:var(--ink);paint-order:stroke;
  stroke:#fff;stroke-width:3.5px;stroke-linejoin:round}
.cov-key{list-style:none;display:flex;flex-wrap:wrap;gap:8px 20px;margin-top:22px;
  font-size:14px;color:var(--ink-2)}
.cov-key i{display:inline-block;width:13px;height:13px;border-radius:4px;
  margin-right:7px;vertical-align:-2px}
.cov-key .k1{background:var(--ice-600);border:1px solid var(--ice-700)}
.cov-key .k2{background:var(--ice-50);border:1px solid var(--ice-300)}
.cov-key .k3{background:var(--warm)}
.cov-list{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-top:24px}
.cov-list a,.cov-list span{display:block;padding:11px 14px;border-radius:var(--r-input);
  border:1px solid var(--line-2);background:#fff;
  text-decoration:none;color:var(--ink);font-size:14.5px;font-weight:600;
  transition:border-color .15s,background .15s}
.cov-list a:hover{border-color:var(--ice-500);background:var(--ice-50)}
/* The collapsed state exists only once the script has said so, so with no
   JavaScript every borough is a plain link in a plain grid. */
.cov-list[data-collapsed] > :nth-child(n+7){display:none}
.cov-list span{color:var(--ink-2)}
.cov-more{display:none;margin-top:12px;min-height:44px;width:100%;
  padding:0 16px;border-radius:var(--r-pill);cursor:pointer;
  border:1.5px solid var(--line-2);background:#fff;
  color:var(--ink);font:700 14px var(--font);transition:background .16s,border-color .16s}
.cov-more:hover{background:var(--ice-50);border-color:var(--ice-500)}
.cov-more[data-on]{display:block}

/* ==========================================================================
   AREAS
   ========================================================================== */
.areas{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:11px}
.areas a{display:flex;align-items:center;justify-content:space-between;gap:10px;
  background:#fff;border:1px solid var(--line);border-radius:var(--r-input);
  padding:15px 18px;text-decoration:none;font-weight:600;font-size:15.5px;min-height:58px;
  transition:border-color .15s,transform .15s,box-shadow .15s}
.areas a:hover{border-color:var(--ice-500);transform:translateY(-2px);
  box-shadow:var(--sh-sm);color:var(--ice-700)}
.areas a span{font-size:12.8px;color:var(--ink-3);font-weight:500}

/* ==========================================================================
   STATS
   ========================================================================== */
.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:22px}
.stat{display:grid;gap:5px}
.stat .n{font-family:var(--display);font-size:clamp(30px,3vw,42px);font-weight:800;
  letter-spacing:-.04em;line-height:1;color:var(--ice-700)}
.stat .l{font-size:14.5px;color:var(--ink-2)}

/* ==========================================================================
   PRICE PANEL
   ========================================================================== */
.price-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--gap);align-items:start}
.price-card{background:#fff;border:1px solid var(--line);border-radius:var(--r-panel);
  padding:clamp(24px,2.4vw,34px)}
.figure{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.figure .stat-n{font-family:var(--display);font-size:clamp(38px,5vw,62px);font-weight:800;
  letter-spacing:-.045em;line-height:1;color:var(--ice-700)}
.figure .unit{color:var(--ink-2);font-size:16px;font-weight:500}
.sub{color:var(--ink-2);font-size:15px;margin-top:10px}
.srcnote{font-size:13.5px;color:var(--ink-2);margin-top:18px;background:var(--paper-50);
  padding:15px 17px;border-radius:var(--r-input)}
.tbar{height:8px;margin-top:22px;border-radius:99px;
  background:linear-gradient(90deg,var(--warm),#F0764A 26%,#D6DEE4 52%,var(--ice-400) 76%,var(--ice-200))}
.tbar-lab{display:flex;justify-content:space-between;font-size:12.8px;color:var(--ink-2);
  margin-top:9px;font-weight:500}
.tbar-lab .w{color:var(--warm-ink)}
.tbar-lab .c{color:var(--ice-700)}
.ptable{width:100%;font-size:15px}
.ptable caption{text-align:left;font-size:14px;font-weight:600;color:var(--ink);padding-bottom:12px}
.ptable th,.ptable td{padding:13px 0;border-bottom:1px solid var(--line);text-align:left}
.ptable thead th{font-size:12.5px;color:var(--ink-2);font-weight:700}
.ptable td:last-child,.ptable th:last-child{text-align:right;font-weight:600;color:var(--ice-700)}
.ptable tbody tr:last-child td{border-bottom:0}

/* ==========================================================================
   CALCULATORS
   ========================================================================== */
.calc{background:#fff;border:1px solid var(--line);border-radius:var(--r-panel);
  box-shadow:var(--sh);overflow:hidden}
.calc-hd{padding:clamp(24px,2.2vw,30px);border-bottom:1px solid var(--line);background:var(--paper-50)}
.calc-hd h2{font-size:var(--h-section)}
.calc-hd p{font-size:14.5px;color:var(--ink-2);margin-top:9px;max-width:58ch}
.calc-body{padding:clamp(24px,2.2vw,30px);display:grid;gap:22px}
.calc .fld > label,.calc .fld > legend{font-size:15px}
.calc-out{border-top:1px solid var(--line);background:var(--ice-50);
  padding:clamp(24px,2.2vw,30px);display:grid;gap:18px}
.calc-out[hidden]{display:none}
.price-row{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}
.price-row .big{font-family:var(--display);font-size:clamp(36px,4.4vw,56px);font-weight:800;
  letter-spacing:-.045em;line-height:1;color:var(--ice-700)}
.price-row .unit{color:var(--ink-2);font-size:16px}
.price-row .tag{font-size:13.5px;font-weight:700;color:var(--ok);background:var(--ok-50);
  border-radius:var(--r-pill);padding:6px 14px}
.calc-note{font-size:13.5px;color:var(--ink-2)}
.calc-cta{display:grid;grid-template-columns:1fr 1fr;gap:11px}
.bd{width:100%;font-size:14.5px;background:#fff;border-radius:var(--r-input);overflow:hidden}
.bd th,.bd td{padding:12px 16px;border-bottom:1px solid var(--line);text-align:left}
.bd thead th{font-size:12.5px;background:var(--paper-50);font-weight:700}
.bd td:last-child,.bd th:last-child{text-align:right;font-weight:600;color:var(--ink)}
.bd tbody tr:last-child td{border-bottom:0}
.bd tfoot td{font-weight:700;color:var(--ice-700);background:var(--paper-50);border-bottom:0}

/* ==========================================================================
   FAQ
   ========================================================================== */
.faq{max-width:80ch;margin-inline:auto;display:grid;gap:10px}
.faq details{background:#fff;border:1px solid var(--line);border-radius:var(--r-panel);
  overflow:hidden;transition:border-color .18s}
.faq details[open]{border-color:var(--ice-400)}
.faq summary{padding:20px 56px 20px 24px;cursor:pointer;font-weight:600;font-size:16.5px;
  list-style:none;position:relative}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"";position:absolute;right:24px;top:50%;width:9px;height:9px;
  border-right:2.2px solid var(--ice-600);border-bottom:2.2px solid var(--ice-600);
  translate:0 -62%;rotate:45deg;transition:rotate .2s ease}
.faq details[open] summary::after{rotate:225deg;translate:0 -25%}
.faq details p{padding:0 24px 20px;color:var(--ink-2);font-size:15.5px}
.faq details p + p{padding-top:0}
.faq details a{color:var(--ice-700);font-weight:500}

/* FAQ page: a jump nav and grouped sections, because thirty questions in one
   undifferentiated accordion is a page nobody reaches the bottom of. */
.faq-nav{display:flex;flex-wrap:wrap;gap:9px;margin-bottom:var(--gap)}
.faq-nav a{display:inline-flex;align-items:center;min-height:42px;padding:0 16px;
  border-radius:var(--r-pill);border:1px solid var(--line-2);background:#fff;
  text-decoration:none;font-size:14px;font-weight:600;color:var(--ink);
  transition:border-color .15s,background .15s,color .15s}
.faq-nav a:hover{border-color:var(--ice-500);background:var(--ice-50);color:var(--ice-700)}
.faq-group{margin-top:var(--band)}
.faq-group:first-of-type{margin-top:0}
.faq-group > h2{margin-bottom:var(--gap)}
.faq-group .faq{margin-inline:0;max-width:none}

/* ==========================================================================
   CLOSING CALL TO ACTION
   A card with a gradient rather than a full bleed
   band. The band version sat directly above a footer that opened with its own
   dark call to action, so every page appeared to end twice.
   ========================================================================== */
.ctacard{position:relative;isolation:isolate;overflow:hidden;
  border-radius:clamp(20px,2.4vw,30px);
  padding:clamp(30px,4vw,58px) clamp(24px,3.4vw,52px);
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:var(--gap);align-items:center;
  background:linear-gradient(118deg,var(--ink-900) 0%,var(--ice-700) 58%,var(--ice-500) 100%);
  box-shadow:0 30px 70px -34px rgba(17,83,111,.6)}
/* The one deliberate exception. It is a section heading, but it sits in a
   card beside a form, so it takes the section size capped lower. */
.ctacard h2{color:#fff;font-size:clamp(25px,2.7vw,36px);max-width:17ch}
/* Scoped to the copy column on purpose. A bare `.ctacard p` also caught the
   reassurance line inside the white form panel and painted it near-white on
   white, which is an invisible line under the one button that matters. */
.ctacard-t p{color:rgba(231,238,244,.9);max-width:50ch;margin-top:12px;
  font-size:clamp(15px,.4vw + 14px,17px)}
.ctacard-b{display:flex;flex-wrap:wrap;gap:11px}
/* On a coloured ground the accent button disappears, so the primary inverts. */
.btn--onbrand{background:var(--ice-300);color:var(--ink)}
.btn--onbrand:hover{background:var(--ice-200);color:var(--ink);
  box-shadow:0 18px 40px -18px rgba(0,0,0,.45)}
/* WHITE ON LIGHT BLUE. This set the colour and the border and never turned
   off .btn's own background, so a "ghost" button on a dark card was rendering
   as white text on the ice-400 fill: 2.13 to 1, worse than either of the two
   colours would be on their own. Unscoped from .ctacard at the same time,
   because a dark-ground ghost is needed on dark heroes too. */
.btn--ghost-d{background:transparent;color:#fff;border-color:rgba(255,255,255,.45)}
.btn--ghost-d:hover{background:rgba(255,255,255,.14);border-color:#fff;color:#fff}
.ctacard-b{margin-top:26px}

/* The form variant. Every page except /contact/ ends on this, because a closing
   block whose only conversion is a link to another page loses the visitor who
   was ready on this one. Left: the argument and the two instant channels.
   Right: four fields on a white panel, because the form styles are written for
   a light ground and re-colouring them for the gradient would fork them. */
.ctacard--form{grid-template-columns:minmax(0,1fr) minmax(340px,400px);
  align-items:start;
  padding:clamp(26px,3.4vw,44px) clamp(22px,3vw,44px)}
.ctacard--form .ctacard-t{align-self:center}
.ctacard--form h2{max-width:14ch}
.ctacard--form .ctacard-t p{max-width:40ch}
.ctacard-form{background:#fff;border-radius:clamp(14px,1.4vw,18px);
  padding:clamp(18px,1.8vw,24px);box-shadow:0 20px 50px -26px rgba(10,17,25,.5)}
.ctacard-form-t{font-family:var(--display);font-weight:800;letter-spacing:-.02em;
  font-size:17px;color:var(--ink);margin:0 0 14px}
.ctacard-form .lead-foot{color:var(--ink-3)}
.ctacard-form .lead-foot.is-err{color:var(--stop)}
.ctacard-form .lead-foot.is-ok{color:var(--ok)}

/* ==========================================================================
   CONTACT
   ========================================================================== */
.contact-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,420px);
  gap:var(--gap);align-items:start}
.cmethod{display:grid;gap:12px}
.cmethod a{display:flex;gap:16px;align-items:center;background:#fff;border:1px solid var(--line);
  border-radius:var(--r-panel);padding:20px 22px;text-decoration:none;
  transition:border-color .15s,transform .15s,box-shadow .15s}
.cmethod a:hover{border-color:var(--ice-500);transform:translateY(-2px);box-shadow:var(--sh-sm)}
.cmethod .ic-lg{flex:0 0 auto}
.cmethod b{display:block;font-size:17px;font-weight:700;color:var(--ink)}
.cmethod span{display:block;font-size:14px;color:var(--ink-2);margin-top:2px}
.cmethod .is-wa .ic-lg{color:#16853F}

.imgcard{border-radius:var(--r-panel);overflow:hidden;border:1px solid var(--line);background:#fff}
.imgcard img{width:100%;aspect-ratio:4/3;object-fit:cover}
.imgcard figcaption{padding:16px 20px;font-size:14px;color:var(--ink-2)}

/* ==========================================================================
   FOOTER
   Three zones with real hierarchy: a closing call to action, then the
   navigation and the company facts side by side, then one quiet legal line.
   The old one was a four column link dump with a table in it.
   ========================================================================== */
/* Light since 11 September 2026, with the rest of the dark bands. The
   footer uses the light-ground logo. */
.ftr{background:var(--paper-50);color:var(--ink-2);font-size:14.5px;border-top:1px solid var(--line)}
.ftr-cta{padding-block:var(--band);border-bottom:1px solid var(--line)}
.ftr-cta-in{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:var(--gap);align-items:center}
.ftr-cta h2{color:var(--ink);font-size:clamp(26px,2.8vw,40px);max-width:17ch}
.ftr-cta p{color:var(--ink-2);max-width:52ch;margin-top:12px}
.ftr-cta-b{display:flex;flex-wrap:wrap;gap:12px}

.ftr-main{padding-block:clamp(34px,3.4vw,50px);
  display:grid;grid-template-columns:minmax(0,1.5fr) repeat(3,minmax(0,1fr));gap:var(--gap)}
.ftr h3{color:var(--ink);font-size:14px;font-weight:700;letter-spacing:.01em;margin-bottom:16px}
.ftr ul{list-style:none;display:grid;gap:11px}
.ftr a{color:var(--ink-2);text-decoration:none}
.ftr a:hover{color:var(--ice-700);text-decoration:underline}
.ftr-about p{margin-top:18px;max-width:38ch;font-size:14px;line-height:1.62}


.ftr-nap{display:grid;gap:9px;margin-top:20px;font-size:14px}
.ftr-nap a{color:var(--ink);font-weight:600}
.ftr-nap span{color:var(--ink-2)}

/* Prefixed with .ftr for the same reason the policy links are: `.ftr ul` is
   two classes and sets display:grid for the footer's link columns, so a bare
   .soc lost and the social buttons stacked into a vertical column. Any list
   dropped into the footer has to out-specify that rule or inherit a grid. */
.ftr .soc{list-style:none;display:flex;gap:9px;margin-top:22px}
.soc a{display:grid;place-items:center;width:42px;height:42px;border-radius:var(--r-pill);
  background:#fff;border:1px solid var(--line-2);
  transition:background .16s,transform .16s,border-color .16s}
.soc a:hover{background:var(--ice-600);border-color:var(--ice-600);transform:translateY(-2px)}
.soc svg{width:18px;height:18px;fill:var(--ink)}
.soc a:hover svg{fill:#fff}

.ftr-legal{border-top:1px solid var(--line);padding-block:20px 26px;
  display:grid;gap:16px;font-size:13px;color:var(--ink-3)}
.ftr-legal p{max-width:96ch;line-height:1.6}
/* `.ftr ul` is two classes and this was one, so the policy links inherited the
   column grid from the footer's link lists and stacked into six rows, which is
   most of what made the footer feel long. */
.ftr .ftr-legal-nav{display:flex;flex-wrap:wrap;gap:8px 22px}

/* ==========================================================================
   WHATSAPP LAUNCHER
   Replaces the sticky bottom bar, which was eating 72px of every phone screen
   on every page.

   It opens a small compose panel rather than jumping straight out to WhatsApp,
   because a wa.me link hands us a message the visitor never wrote, and on
   desktop it throws them into WhatsApp Web with no idea what happened. Here
   they edit a draft and press send, so what arrives is in their words.
   ========================================================================== */
.wa-fab{position:fixed;right:clamp(14px,2vw,26px);bottom:clamp(14px,2vw,26px);z-index:96;
  width:58px;height:58px;border:0;border-radius:var(--r-pill);cursor:pointer;
  background:#16853F;color:#fff;display:grid;place-items:center;padding:0;
  box-shadow:0 14px 34px -10px rgba(31,168,85,.55),0 4px 10px -4px rgba(10,17,25,.4);
  transition:transform .18s ease,background .18s ease}
.wa-fab svg{width:28px;height:28px}
.wa-fab:hover{background:#0F6D32;transform:translateY(-2px) scale(1.04)}
.wa-fab[aria-expanded=true]{transform:scale(.9)}

/* The nudge. Appears once, a few seconds in, and not again in that session. */
/* Clears the WhatsApp button. */
.wa-panel{position:fixed;right:clamp(14px,2vw,26px);
  bottom:calc(clamp(14px,2vw,26px) + 70px);z-index:97;
  width:min(360px,calc(100vw - 28px));background:var(--paper-50);
  border-radius:var(--r-panel);overflow:hidden;box-shadow:var(--sh-lg);
  border:1px solid var(--line);animation:wa-pop .34s cubic-bezier(.16,1,.3,1) both}
.wa-panel[hidden]{display:none}
.wa-head{display:flex;align-items:center;gap:11px;padding:14px 16px;background:#16853F;color:#fff}
.wa-avatar{display:grid;place-items:center;width:36px;height:36px;flex:0 0 auto;
  border-radius:var(--r-pill);background:rgba(255,255,255,.2)}
.wa-avatar svg{width:19px;height:19px}
.wa-id{display:grid;gap:1px;flex:1;min-width:0}
.wa-id strong{font-size:14.5px;font-weight:700}
.wa-id small{font-size:12px;opacity:.85}
.wa-close{width:30px;height:30px;border:0;background:none;color:#fff;cursor:pointer;
  border-radius:var(--r-pill);display:grid;place-items:center;flex:0 0 auto}
.wa-close:hover{background:rgba(255,255,255,.18)}
.wa-close svg{width:14px;height:14px}
.wa-body{padding:18px 16px 8px;background:#EDE5DC}
.wa-bubble{background:#fff;border-radius:4px 14px 14px 14px;padding:12px 14px;
  box-shadow:0 1px 2px rgba(10,17,25,.14);max-width:92%}
.wa-bubble p{font-size:14.5px;line-height:1.5;color:var(--ink)}
.wa-from{display:block;margin-top:5px;font-size:11px;color:var(--ink-3)}
.wa-compose{padding:12px 14px 14px;background:#EDE5DC;display:grid;gap:9px}
.wa-compose textarea{width:100%;border:1px solid var(--line-2);border-radius:14px;
  padding:11px 13px;font:400 14.5px/1.5 var(--font);color:var(--ink);background:#fff;
  resize:vertical;min-height:76px}
.wa-compose textarea:focus{outline:none;border-color:#16853F;
  box-shadow:0 0 0 3px rgba(31,168,85,.18)}
.wa-compose-row{display:flex;align-items:center;justify-content:space-between;gap:10px}
.wa-hint{font-size:12px;color:var(--ink-2)}
.wa-send{display:inline-flex;align-items:center;gap:7px;min-height:42px;padding:0 18px;
  border:0;border-radius:var(--r-pill);background:#16853F;color:#fff;
  font:700 14px var(--font);cursor:pointer;transition:background .16s}
.wa-send:hover{background:#0F6D32}
.wa-send svg{width:15px;height:15px}

/* ==========================================================================
   UTILITIES
   ========================================================================== */
.center{text-align:center}
.muted{color:var(--ink-2)}
.small{font-size:14px}
.pill{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:700;
  color:var(--ice-700);background:var(--ice-50);border-radius:var(--r-pill);
  padding:7px 15px;width:max-content}
.pill--warm{color:var(--warm-ink);background:var(--warm-50)}
.pill--ok{color:var(--ok);background:var(--ok-50)}
.gallery{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}
.gallery img{border-radius:var(--r-input);aspect-ratio:3/4;object-fit:cover;width:100%;
  border:1px solid var(--line)}

/* ==========================================================================
   RESPONSIVE
   Designed at 375 first. Every multi column layout declares its collapse.
   ========================================================================== */
@media (max-width:1460px){
  .nav-tel{display:none}
}
@media (max-width:1180px){
  .navlinks > li > a{padding:10px 7px;font-size:13.5px}
  .hero-grid{grid-template-columns:1fr;align-items:start}
  .hero h1{max-width:18ch}
  .pcheck{max-width:560px}
  .cov{grid-template-columns:1fr}
  .sub{min-width:290px}
  .phero-grid{grid-template-columns:1fr}
  .quotecard{max-width:520px}
  .pcheck-grid{grid-template-columns:1fr}
  .pcheck{max-width:560px}
}
@media (max-width:1080px){
  .cards,.cards--4,.steps,.stats,.areas,.qcards,.sys{grid-template-columns:repeat(2,1fr)}
  .svc{grid-template-columns:repeat(2,1fr);grid-auto-rows:13rem}
  .svc-card--feature,.svc-card--wide{grid-column:span 2;grid-row:span 2}
  .svc-card--tall{grid-column:span 1;grid-row:span 2}
  .creds-list{grid-template-columns:repeat(2,1fr)}
  .steps li + li{border-left:0}
  .steps li:nth-child(even){border-left:1px solid var(--line)}
  .steps li:nth-child(n+3){border-top:1px solid var(--line)}
  .split,.contact-grid,.price-grid{grid-template-columns:1fr}
  .sticky-side{position:static}
  .ctacard,.ctacard--form{grid-template-columns:1fr}
  .ctacard-b{margin-top:22px}
  .ftr-main{grid-template-columns:1fr 1fr}
  .gallery{grid-template-columns:repeat(3,1fr)}
}
@media (max-width:940px){
  .navlinks,.nav-tel{display:none}
  .burger{display:grid}
  .nav{padding-left:16px}
  .estbar-chips{display:none}
}
@media (max-width:720px){
  /* The capsule is 64px here and holds two things: the logo and the burger.
     Both were too small and the burger sat too far in from the edge, which is
     what made the three lines look dropped in at random rather than placed.
     The bar now runs closer to the screen edge (10px, not the page --pad), the
     logo is 44px instead of 36, and the burger is flush against the capsule's
     own 6px of padding so its right edge lands 16px from the screen. */
  :root{--nav-h:64px}
  .navwrap{top:10px}
  .navwrap > .wrap{width:calc(100% - 20px)}
  .nav{padding:6px 6px 6px 14px;gap:6px}
  .logo{height:36px}
  .burger{width:52px;height:52px}
  .nav-cta{display:none}
  .hero{padding-top:calc(var(--nav-h) + 40px)}
  .hero-in{max-width:none}
  .hero h1{font-size:clamp(32px,8.6vw,44px)}
  .hero p{font-size:16px;margin-top:16px}
  .hero-cta{margin-top:24px}
  .hero-cta .btn{flex:1 1 100%}
  /* The call button goes: the number is in the strip at the top of every page
     and in the WhatsApp button beside this one, and what a phone visitor needs
     here is the way down to the form. */
  .btn--callonly{display:none}
  .btn--jump{display:inline-flex}
  .only-narrow{display:inline}
  .only-wide{display:none}
  .hero-form-hd h2{font-size:19px}
  .hero-form-hd p{font-size:13px}
  /* Three buttons at three different widths reads as an accident. On a
     phone they stack, so they take the full width like the hero pair. */
  .ctacard-b .btn{flex:1 1 100%}
  /* On a phone the copy runs the full width, so the veil has to be vertical
     rather than horizontal: dark where the headline and the two buttons are,
     and then it lets go for the strip above the form, which is where the clip
     gets to be seen. */
  .hero-veil,.phero-veil{background:
    linear-gradient(180deg,rgba(10,17,25,.86) 0%,rgba(10,17,25,.8) 34%,rgba(10,17,25,.5) 58%,rgba(10,17,25,.22) 78%,rgba(10,17,25,.55) 100%)}
  .estbar-in{padding:16px 18px}
  .phero{padding-top:calc(var(--nav-h) + 40px)}
  .phero-cta .btn{flex:1 1 100%}
  .cards,.cards--2,.cards--4,.steps,.areas,.qcards,.sys{grid-template-columns:1fr}
  /* THE TEN YEARS BLOCK ON A PHONE.

     David: it has far too much writing, make it look completely different or
     take it out. On a desktop the copy sits beside the photographs and reads as
     one object; stacked on a phone it became roughly 400 words in a column,
     which nobody scrolls.

     So on a phone it is a different section rather than the same one narrower.
     The photographs come first, because they are the argument. The long second
     paragraph goes: it repeats the company detail that is already in the footer
     and on the about page. The three arguments underneath become a swipe row,
     the same shape as the services belt, so they cost one screen instead of
     three. Nothing is deleted from the page, it is re-shaped. */
  /* The order David asked for: heading, one line, the two buttons, the table,
     then the photographs. `display: contents` on the copy column is what makes
     it possible at all: the buttons and the table live inside .tenyr-copy and
     the photographs are its sibling, so without dissolving that wrapper there
     is no single flow to order them in. */
  .tenyr{grid-template-columns:1fr;gap:0}
  .tenyr-copy{display:contents}
  .tenyr-copy h2{order:1}
  .tenyr-tabs{order:2;margin-top:14px}
  .tenyr-panes{order:3}
  .tenyr-pane > .lede{font-size:16px;margin-top:14px}
  .tenyr-pane > .tenyr-text{display:none}
  .tenyr-cta{order:4;margin-top:18px}
  .tenyr-facts{grid-template-columns:1fr 1fr;margin-top:16px}
  .tenyr-facts li{padding:13px 14px}
  .tenyr-facts span{font-size:12.5px}
  .wall{order:5;height:300px;max-width:none;margin-top:18px}
  .accs{order:6;gap:12px;margin-top:18px}
  .accs span{font-size:13px}

  /* Both buttons on one row, so they read as a pair rather than a stack. They
     shrink to do it: 1 1 0 shares the width equally whatever the labels are. */
  .tenyr-cta{display:flex;flex-wrap:nowrap;gap:9px}
  .tenyr-cta .btn{flex:1 1 0;min-width:0;min-height:48px;padding:0 10px;
    font-size:13.5px;white-space:nowrap}

  /* Folded on every width now, so the phone needs no special shape for them. */
  .says{order:7;margin-top:18px}
  .say > summary{padding:14px 15px;font-size:16px}
  .say > p{padding:0 15px 15px;font-size:14px}
  /* THE BENTO ON A PHONE.

     Same idea as the desktop grid, half the columns. Six services stacked full
     width ran to about five screens, which is what the belt was for; a two
     column mosaic does the same job in one screen and keeps the thing that made
     the desktop version work, which is that the tiles are not all the same size.

     The pattern fills exactly, no holes:

        row 1-2   [ 1 . . . . . . . 1 ]   air conditioning, the headline
        row 3     [ 2 ] [ 3 ]             ducted, servicing
        row 4-5   [ 4 . . . . . . . 4 ]   heat pumps, the other money service
        row 6     [ 5 ] [ 6 ]             solar, battery

     Placed by position rather than by the span classes, because four of the six
     services share `--tall` and the phone layout needs to tell them apart. If a
     service is added or reordered, this block is where it shows. */
  /* 8.6rem, not 8. At 8 the one small tile whose name wraps to two lines,
     "Ducted air conditioning", had its price label pressed against it. Half a
     rem across four small tiles costs 20px of page and fixes all of them. */
  .svc{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:8.6rem;gap:10px}
  .svc-card,.svc-card--feature,.svc-card--wide,.svc-card--tall{
    grid-column:span 1;grid-row:span 1}
  .svc-card:nth-child(1),.svc-card:nth-child(4){grid-column:span 2;grid-row:span 2}
  .svc-body{padding:14px}
  .svc-name{font-size:16px}
  .svc-card:nth-child(1) .svc-name,
  .svc-card:nth-child(4) .svc-name{font-size:clamp(20px,5.6vw,24px)}
  .svc-price{font-size:11.5px;line-height:1.3;margin-bottom:5px}
  /* The summary only has room on the two big tiles. */
  .svc-sum{display:none}
  .svc-card:nth-child(1) .svc-sum,
  .svc-card:nth-child(4) .svc-sum{display:block;font-size:13.5px;margin-top:6px}
  .svc-cue{font-size:12.5px;white-space:nowrap}
  .svc-card--tall .svc-sum{display:block}
  /* Two columns on a phone as well, not one. Eleven boroughs in a single
     column is eleven rows of one word in a section that is mostly a map, and
     David's note is that it stretches the section for nothing. Two columns
     halves it, and the show more button halves it again until it is wanted. */
  .cov-list{grid-template-columns:1fr 1fr;gap:7px}
  .cov-list a,.cov-list span{padding:10px 11px;font-size:13.5px}
  .creds-list,.stats{grid-template-columns:1fr 1fr}
  .steps li + li{border-left:0;border-top:1px solid var(--line)}
  .steps li:nth-child(even){border-left:0}
  .ftr-main{grid-template-columns:1fr;gap:34px}
  .calc-cta,.verdict-cta{grid-template-columns:1fr}
  .gallery{grid-template-columns:repeat(2,1fr)}
  .hl{white-space:normal}
  .answer{border-radius:var(--r-panel)}
  /* On a phone the same card goes to one column and the price leads, because
     the DOM order is result, questions, stepper. */
  .pcfg{grid-template-columns:minmax(0,1fr);gap:0;padding:14px 14px 16px}
  .pcfg-qs{grid-template-columns:minmax(0,1fr);order:0;gap:10px;margin-top:12px}
  .pcfg-row-l{font-size:16px}
  .pcfg .stepnav{order:0;margin-top:12px}
  /* Not sticky on a phone. The panel is a price, a breakdown and two buttons:
     roughly 250px, a third of the screen, and pinning it left the current
     question underneath it with nothing visible but Back and Next. It sits at
     the top of the card and scrolls with everything else.

     The inverted stepper furniture that used to be here was NOT scoped, so it
     was also repainting the Back and Next buttons of the running cost and
     sizing calculators, which sit on white. White on white. Those rules are on
     `.pcfg` now, where the gradient they were written for actually is. */
  .pcfg-out{order:0;position:static;border-radius:18px;padding:18px}

}
@media (max-width:420px){
  .seg{grid-template-columns:1fr}
  .creds-list{grid-template-columns:1fr}
}

/* ==========================================================================
   MOTION
   Instructed policy: animations run on any desktop regardless of the OS
   setting, and the preference is honoured on touch and narrow only.
   memory/motion-policy-desktop-override.md
   ========================================================================== */
@media (prefers-reduced-motion:reduce) and (max-width:899px){
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
}
@media (prefers-reduced-motion:reduce) and (pointer:coarse){
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
@media (prefers-reduced-transparency:reduce){
  .nav{background:rgba(15,26,36,.97);backdrop-filter:none;-webkit-backdrop-filter:none}
  .estbar-in{background:#fff;backdrop-filter:none;-webkit-backdrop-filter:none}
}
@media print{.navwrap,.sheet,.wa-fab,.wa-panel,.wa-scrim,.crumbs,.ctacard{display:none!important}}

/* ------------------------------------------------------- installations grid
   The photographic record at /installations/. Three across on desktop, two on
   a tablet, one on a phone. The photograph is 3:2 and cropped in the build, so
   the card never has to letterbox and every image in a row is exactly the same
   height whatever the original camera did. */
.insts{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-top:8px}
.inst{background:var(--paper);border:1px solid var(--line);border-radius:var(--r-panel);
  overflow:hidden;display:grid;grid-template-rows:auto 1fr;
  transition:transform .2s ease,box-shadow .2s ease,border-color .2s}
.inst:hover{transform:translateY(-3px);box-shadow:var(--sh);border-color:var(--ice-400)}
.inst-ph{aspect-ratio:3/2;background:var(--paper-100);overflow:hidden}
.inst-ph img{width:100%;height:100%;object-fit:cover;display:block}
.inst-b-in{padding:18px 20px 20px;display:grid;gap:7px;align-content:start}
/* The kind of job, small and quiet. It is a label, not a heading, so it does
   not compete with the place name underneath it. */
/* `.inst p` has to come FIRST and the two labels have to out-specify it.
   Written the other way round, `.inst p` at (0,1,1) beat `.inst-k` at (0,1,0),
   both labels rendered as body text and the hierarchy the card is built on
   silently did not happen. Contrast was fine either way, which is exactly why
   it would have shipped unnoticed. */
.inst p{font-size:14.5px;color:var(--ink-2);margin:0;line-height:1.55}
.inst .inst-k{font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;
  font-weight:800;color:var(--ice-700);margin:0}
.inst h3{font-size:var(--h-card);margin:0}
.inst .inst-s{font-size:14px;font-weight:700;color:var(--ink);margin:0}
.inst-b{justify-self:start;margin-top:4px;font-size:13px;font-weight:700;
  text-decoration:none;color:var(--ice-700);border-bottom:1px solid var(--ice-400)}
.inst-b:hover{color:var(--ink)}
.inst-b--flat{color:var(--ink-2);border-bottom-color:transparent}
@media (max-width:1000px){.insts{grid-template-columns:repeat(2,1fr)}}
/* Two to a row on a phone, 12 September 2026. One to a row, nineteen jobs were
   6.6 screens, each a photograph and a paragraph. Two to a row with the note
   held to three lines halves that, and every card still opens to its own
   anchor and, where it has one, the district page with the whole story. */
@media (max-width:640px){
  .insts{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
  .inst-ph{aspect-ratio:1/1}
  .inst-b-in{padding:10px 11px 12px;gap:4px}
  .inst h3{font-size:15px;line-height:1.25}
  .inst .inst-k{font-size:10px;letter-spacing:.06em}
  .inst .inst-s{font-size:12.5px;line-height:1.35}
  .inst p{font-size:12.5px;line-height:1.45;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
  .inst-b{font-size:12px}
}
/* Only ever set by the script in main.js, so a page without JavaScript, and
   every crawler, sees all of the cards. */
.insts.is-clipped .inst:nth-child(n+10){display:none}
.inst-more{margin-top:16px}

/* -------------------------------------------------------- the prices chooser
   Four calculators, one visible. The panels are all in the document, so a
   crawler and an answer engine see every set of figures; only the display
   changes. The bar sticks under the navigation on the way down the page,
   because the whole point of the page is switching between services and
   scrolling back up to do it is the friction that kills it. */
/* THE HERO CHOOSER, 11 September 2026. See the note in build/content/prices.mjs.
   White, on David's call the same day: the first version sat on a dark ground
   with a faint grid and two drifting glows, and he was right that it read as
   a tech product rather than a heating engineer. The people pricing a heat
   pump here are homeowners, many of them past sixty. Plain white, dark text,
   cards with a border, and the photograph doing the visual work. */
.pxhero{position:relative;background:#fff;color:var(--ink);
  padding:calc(var(--nav-h) + 58px) 0 clamp(40px,5vw,72px);border-bottom:1px solid var(--line)}
.pxhero-grid{display:grid;grid-template-columns:minmax(0,1.08fr) minmax(0,.92fr);
  gap:clamp(28px,4.4vw,72px);align-items:center}
.pxhero-eye{display:inline-flex;align-items:center;gap:9px;margin:0 0 14px;
  font-size:14px;font-weight:600;color:var(--ice-700)}
.pxhero-eye span{width:8px;height:8px;border-radius:50%;background:var(--ice-600)}
.pxhero h1{color:var(--ink);font-size:clamp(40px,5vw,66px);line-height:1.03;letter-spacing:-.025em}
.pxhero h1 .hl{color:var(--ice-600)}
.pxhero-lede{margin:14px 0 0;font-size:18px;line-height:1.5;color:var(--ink-2);max-width:56ch}

/* The four options. A button each: pressed is the state, and the highlight
   is a single element that moves to the chosen one. */
.pxsel{position:relative;display:grid;gap:10px;margin-top:28px}
.pxsel-glide{position:absolute;left:0;top:0;z-index:0;border-radius:var(--r-panel);
  background:var(--ice-50);border:2px solid var(--ice-500);
  box-shadow:0 14px 30px -18px rgba(30,134,188,.55);
  opacity:0;pointer-events:none}
.pxsel.is-ready .pxsel-glide{opacity:1;
  transition:transform .45s cubic-bezier(.2,.85,.25,1),width .45s cubic-bezier(.2,.85,.25,1),height .45s cubic-bezier(.2,.85,.25,1)}
.pxsel-o{position:relative;z-index:1;display:grid;grid-template-columns:54px minmax(0,1fr) auto 30px;
  align-items:center;gap:16px;padding:14px 16px 14px 14px;text-align:left;
  font:inherit;color:var(--ink);cursor:pointer;border-radius:var(--r-panel);
  border:1.5px solid var(--line-2);background:#fff;
  transition:border-color .2s ease,background .2s ease,transform .2s ease}
.pxsel-o:hover{border-color:var(--ice-400);background:var(--paper-50)}
.pxsel-o:active{transform:scale(.99)}
.pxsel-o:focus-visible{outline:3px solid var(--ice-500);outline-offset:3px}
.pxsel-o[aria-pressed="true"]{border-color:transparent;background:transparent}
/* Without the script there is no glide, so the pressed option paints itself. */
.pxsel:not(.is-ready) .pxsel-o[aria-pressed="true"]{border-color:var(--ice-500);background:var(--ice-50)}
.pxsel-ic{width:54px;height:54px;border-radius:16px;display:grid;place-items:center;
  background:var(--ice-100);color:var(--ice-800);--mi-accent:var(--ice-500);
  transition:background .25s ease,color .25s ease}
.pxsel-ic svg{width:27px;height:27px}
.pxsel-o[aria-pressed="true"] .pxsel-ic{background:var(--ice-600);color:#fff;--mi-accent:#fff}
.pxsel-t{display:flex;flex-direction:column;gap:3px;min-width:0}
.pxsel-t b{font-family:var(--display);font-size:19px;font-weight:700;letter-spacing:-.01em;line-height:1.15}
.pxsel-t small{font-size:14px;color:var(--ink-2);line-height:1.35}
.pxsel-p{display:flex;flex-direction:column;align-items:flex-end;gap:1px;white-space:nowrap}
/* --ink-2, not --ink-3: on the selected chip's tinted ground the lighter
   shade measured 4.43:1 against a 4.5 requirement. */
.pxsel-p small{font-size:12px;font-weight:600;color:var(--ink-2)}
.pxsel-p b{font-family:var(--display);font-size:21px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--ink)}
.pxsel-go{width:30px;height:30px;border-radius:50%;display:grid;place-items:center;
  color:var(--ink-3);border:1.5px solid var(--line-2);
  transition:background .2s ease,color .2s ease,border-color .2s ease,transform .2s ease}
.pxsel-go svg{width:14px;height:14px}
.pxsel-o:hover .pxsel-go{transform:translateX(2px)}
.pxsel-o[aria-pressed="true"] .pxsel-go{background:var(--ice-600);border-color:var(--ice-600);color:#fff}

/* The card: a photograph of the job, the range, three facts and two buttons.
   White, with a border and a soft shadow, like every other card on the site. */
.pxprev{position:relative;border-radius:var(--r-panel);overflow:hidden;background:#fff;
  border:1px solid var(--line);box-shadow:var(--sh-lg)}
.pxprev-media{position:relative;aspect-ratio:16/9;overflow:hidden;background:var(--paper-100)}
.pxprev-media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity .5s ease}
.pxprev-media img.is-on{opacity:1}
.pxprev-tag{position:absolute;left:14px;bottom:14px;z-index:1;display:inline-flex;align-items:center;gap:8px;
  padding:8px 14px;border-radius:var(--r-pill);font-size:14px;font-weight:600;color:var(--ink);
  background:#fff;box-shadow:0 6px 18px -8px rgba(17,26,34,.45)}
.pxprev-tag::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--ice-600)}
.pxprev-body{padding:20px 24px 24px}
.pxprev-l{margin:0;font-size:15px;color:var(--ink-2)}
.pxprev-price{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;margin:6px 0 0}
.pxprev-price b{font-family:var(--display);font-size:clamp(34px,3.3vw,46px);font-weight:800;
  line-height:1.05;letter-spacing:-.02em;font-variant-numeric:tabular-nums;color:var(--ink)}
.pxprev-price span{font-size:17px;color:var(--ink-3)}
.pxprev-facts{list-style:none;margin:16px 0 0;padding:16px 0 0;display:grid;gap:10px;
  border-top:1px solid var(--line)}
.pxprev-facts li{position:relative;padding-left:30px;font-size:15.5px;line-height:1.4;color:var(--ink)}
.pxprev-facts li::before{content:"";position:absolute;left:0;top:1px;width:20px;height:20px;border-radius:50%;
  background:var(--ice-100)}
.pxprev-facts li::after{content:"";position:absolute;left:7px;top:5px;width:5px;height:10px;
  border:solid var(--ice-700);border-width:0 2px 2px 0;transform:rotate(45deg)}
@media (prefers-reduced-motion:no-preference){
  .pxprev-facts.is-new li{animation:pxFact .35s ease both}
  .pxprev-l.is-new,.pxprev-tag.is-new{animation:pxFact .35s ease both}
}
@keyframes pxFact{from{opacity:0}to{opacity:1}}
.pxprev-cta{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);gap:10px;margin-top:20px}
.pxprev-cta .btn{padding:0 18px}
.pxprev-cta .btn svg{width:16px;height:16px}

@media (max-width:1100px){
  .pxsel-o{grid-template-columns:48px minmax(0,1fr) auto;gap:13px}
  .pxsel-ic{width:48px;height:48px;border-radius:14px}
  .pxsel-go{display:none}
}
/* A phone. Two by two, with the price under the name, and the card below.
   Tall enough to tap without looking, short enough that the price is still
   on the first screen at 375 by 812. */
@media (max-width:900px){
  .pxhero{padding-top:calc(var(--nav-h) + 34px)}
  .crumbs + .pxhero{padding-top:20px}
  .pxhero-grid{grid-template-columns:minmax(0,1fr);gap:20px}
  .pxhero-eye{font-size:12px;margin-bottom:12px}
  .pxhero h1{font-size:clamp(34px,9vw,52px)}
  .pxhero-lede{font-size:15.5px;margin-top:10px}
  .pxsel{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:18px}
  .pxsel-o{grid-template-columns:minmax(0,1fr);align-content:start;gap:7px;padding:12px 12px 11px;min-height:0}
  .pxsel-ic{width:36px;height:36px;border-radius:11px}
  .pxsel-ic svg{width:21px;height:21px}
  .pxsel-t b{font-size:15.5px}
  .pxsel-t small{display:none}
  .pxsel-p{flex-direction:row;align-items:baseline;gap:5px}
  .pxsel-p b{font-size:16.5px}
  .pxprev-media{aspect-ratio:5/2}
  .pxprev-body{padding:16px 18px 18px}
  .pxprev-price b{font-size:clamp(30px,8.4vw,40px)}
}
@media (max-width:480px){
  .pxprev-cta{grid-template-columns:minmax(0,1fr)}
  .pxprev-facts li{font-size:14px}
}

/* The same four choices, small, for the rest of the page. Without the script
   it is an ordinary centred bar under the hero; the script floats it under the
   navigation and only shows it once the chooser above is off screen. */
.pxtabs-wrap{display:flex;justify-content:center;padding:14px 10px;background:var(--paper-50);
  border-bottom:1px solid var(--line)}
.pxtabs-wrap.is-float{position:fixed;left:50%;top:calc(var(--nav-h) + 26px);z-index:90;
  padding:0;background:none;border:0;max-width:calc(100% - 20px);
  translate:-50% -12px;opacity:0;visibility:hidden;
  transition:opacity .25s ease,translate .35s cubic-bezier(.2,.8,.25,1),visibility .25s}
.pxtabs-wrap.is-float.is-on{opacity:1;visibility:visible;translate:-50% 0}
.pxtabs{display:flex;gap:4px;padding:5px;border-radius:var(--r-pill);overflow-x:auto;max-width:100%;
  background:#fff;border:1px solid var(--line-2);
  box-shadow:0 14px 34px -16px rgba(17,26,34,.35);
  scrollbar-width:none;-ms-overflow-style:none}
.pxtabs::-webkit-scrollbar{display:none}
.pxtab{flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;border:0;background:transparent;
  color:var(--ink-2);border-radius:var(--r-pill);padding:9px 16px 9px 12px;font:inherit;
  font-size:14px;font-weight:650;cursor:pointer;white-space:nowrap;
  transition:background .2s ease,color .2s ease}
.pxtab:hover{background:var(--paper-50);color:var(--ink)}
.pxtab[aria-selected="true"]{background:var(--ice-700);color:#fff}
.pxtab-ic{display:grid;place-items:center;--mi-accent:currentColor}
.pxtab-ic svg{width:18px;height:18px}
.pxtab-short{display:none}
.pxpanel{scroll-margin-top:calc(var(--nav-h) + 92px)}
.pxpanel-l{max-width:62ch;margin:22px auto 0;text-align:center;
  color:var(--ink-2);font-size:15.5px}
/* The panel fades and lifts a few pixels when it is swapped in. Six frames of
   movement, no library, and it is skipped entirely under reduced motion. */
@media (prefers-reduced-motion:no-preference){
  .pxpanel[data-enter]{animation:pxin .26s cubic-bezier(.2,.7,.3,1) both}
}
@keyframes pxin{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.phero--tight{min-height:auto}
.phero--tight .phero-grid{padding-block:clamp(38px,6vw,64px)}
@media (max-width:640px){
  .pxtab{padding:9px 13px 9px 10px;font-size:13.5px;gap:6px}
  .pxtab-full{display:none}
  .pxtab-short{display:inline}
}
@media (max-width:400px){
  .pxtab-ic{display:none}
  .pxtab{padding:9px 12px}
}

/* ------------------------------------------------- the job on a district page
   Photograph beside the story of the job we did in that postcode. It is the
   thing that makes a district page a page rather than a doorway, so it sits
   directly under the answer and above the housing stock. */
.jobshow{display:grid;grid-template-columns:minmax(0,460px) minmax(0,1fr);
  gap:clamp(22px,3vw,40px);align-items:center}
.jobshow-ph{border-radius:var(--r-panel);overflow:hidden;background:var(--paper-100);
  aspect-ratio:3/2}
.jobshow-ph img{width:100%;height:100%;object-fit:cover;display:block}
.jobshow h2{margin:2px 0 0}
.jobshow > div > p{margin:7px 0 0;color:var(--ink-2);font-size:15.5px;line-height:1.6}
.jobshow .inst-k{margin:0}
.jobshow .inst-s{color:var(--ink);font-weight:700;font-size:15px}
@media (max-width:860px){.jobshow{grid-template-columns:1fr}}

/* ------------------------------------------------------------ before and after
   The wipe is a single custom property, so the range input can drive it with no
   script at all and the script only adds pointer dragging on top. */
.ba{--wipe:50%;max-width:760px;margin-inline:auto}
.ba-stack{position:relative;aspect-ratio:3/2;border-radius:var(--r-panel);
  overflow:hidden;background:var(--paper-100);touch-action:pan-y;cursor:ew-resize}
.ba-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
/* clip-path rather than width, so the BEFORE image never rescales as it is
   revealed. Animating width squashes the photograph and it looks wrong the
   moment you drag quickly. */
.ba-clip{position:absolute;inset:0;clip-path:inset(0 calc(100% - var(--wipe)) 0 0)}
.ba-line{position:absolute;top:0;bottom:0;left:var(--wipe);width:2px;
  background:rgba(255,255,255,.92);transform:translateX(-1px);
  box-shadow:0 0 0 1px rgba(10,17,25,.28)}
.ba-grip{position:absolute;top:50%;left:50%;width:40px;height:40px;
  transform:translate(-50%,-50%);border-radius:999px;background:#fff;
  box-shadow:0 3px 14px rgba(10,17,25,.32)}
.ba-grip::before,.ba-grip::after{content:"";position:absolute;top:50%;width:0;height:0;
  border-block:5px solid transparent}
.ba-grip::before{left:11px;border-right:7px solid var(--ink);transform:translateY(-50%)}
.ba-grip::after{right:11px;border-left:7px solid var(--ink);transform:translateY(-50%)}
.ba-tag{position:absolute;top:12px;padding:5px 11px;border-radius:999px;
  font-size:11.5px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;
  background:rgba(10,17,25,.72);color:#fff;backdrop-filter:blur(4px)}
.ba-tag--b{left:12px}
.ba-tag--a{right:12px}
.ba-ctl{display:grid;gap:7px;margin-top:14px}
.ba-ctl-l{font-size:13px;font-weight:700;color:var(--ink-2);text-align:center}
.ba-range{width:100%;accent-color:var(--ice-600)}
.ba-range:focus-visible{outline:3px solid var(--ice-400);outline-offset:3px;border-radius:999px}
@media (prefers-reduced-motion:no-preference){
  .ba-line,.ba-clip{transition:none}
}

/* ------------------------------------------------------------ menu icon accent
   The brand icons are two layers, and the second is this. On the pale dropdown
   tile the accent is the deeper cyan so it holds up against ice-100; on the
   dark feature card it is the bright one. */
.mi-ic{--mi-accent:var(--ice-500)}
.mfeat-ic{--mi-accent:#41C7FE}
.mi-acc{stroke:var(--mi-accent)}

/* ------------------------------------------------------------- the bar's width
   Measured on 11 September 2026 at every width from 360 to 1920, after David
   reported the phone number and the survey button running off the right edge
   of the screen. The six links, the logo and the button need 1,029 to 1,085
   pixels. The capsule gives them 811 at 941px and does not reach that until
   about 1,240. Between the two the button was being pushed out of the capsule
   and, below about 1,100, out of the viewport.

   Two changes, both measured rather than guessed:

   1. The burger takes over up to 1,180px instead of 940. A laptop at 1,024 or
      a tablet in landscape gets the full menu in the sheet, which is where it
      fits, and keeps the survey button in the bar.
   2. From 1,181 to 1,320 the links, the logo and the button tighten. That buys
      about 70 pixels, and the tightest case, 1,181, needs 53. */
@media (max-width:1180px){
  .navlinks{display:none}
  .burger{display:grid}
}
@media (min-width:1181px) and (max-width:1320px){
  .navlinks > li > a{padding:10px 7px;font-size:14px}
  .logo{height:38px}
  .nav-cta{padding:0 20px}
}
/* The smallest phones still sold, and the Galaxy fold's cover screen. At 320
   the 34px logo and the 52px burger needed 301 pixels of a 300 pixel capsule. */
@media (max-width:360px){
  .logo{height:32px}
}

/* ------------------------------------------------------------ projects belt
   The home page slider, on white as David asked. A continuous loop driven by
   belt() in main.js: the view is a real horizontal scroller, so a thumb stops
   it and can push it, a mouse over it slows it to a reading pace, and the
   scrollbar is hidden because the motion is the affordance.

   The edges fade into the white rather than cutting off. Without the fade a
   card sliced in half at the viewport edge reads as a layout bug; with it the
   row reads as a window onto something that continues. */
.band--white{background:#fff}
.proj-hd{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;
  flex-wrap:wrap;margin-bottom:26px}
.proj-hd h2{margin:0}
.proj-hd .lede{margin:8px 0 0}
.proj-all{display:inline-flex;align-items:center;gap:8px;font-weight:700;font-size:15px;
  color:var(--ice-700);text-decoration:none;padding:10px 0;border-bottom:1.5px solid var(--ice-400)}
.proj-all svg{width:16px;height:16px;transition:transform .2s ease}
.proj-all:hover svg{transform:translateX(3px)}

.proj-view{overflow-x:auto;overflow-y:hidden;scrollbar-width:none;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
  padding:6px 0 18px;outline:0}
.proj-view::-webkit-scrollbar{display:none}
.proj-view:focus-visible{outline:3px solid var(--ice-400);outline-offset:-3px;border-radius:12px}
/* Track and group carry the same gap, so the seam between the real group and
   its clone is identical to the space between any two cards and the loop has
   no visible join. belt() reads the gap to measure one group's width. */
.proj-track,.proj-group{display:flex;gap:20px}
.proj-track{width:max-content;padding-inline:max(var(--pad),calc((100vw - 1340px) / 2))}

.pcard{flex:0 0 320px;display:flex;flex-direction:column;border-radius:20px;overflow:hidden;
  background:#fff;border:1px solid var(--line);text-decoration:none;color:inherit;
  box-shadow:0 1px 2px rgba(15,26,36,.04),0 12px 28px -18px rgba(15,26,36,.28);
  transition:transform .28s cubic-bezier(.2,.7,.3,1),box-shadow .28s ease,border-color .28s ease}
.pcard:hover,.pcard:focus-visible{transform:translateY(-5px);border-color:var(--ice-400);
  box-shadow:0 2px 4px rgba(15,26,36,.05),0 24px 44px -20px rgba(22,107,153,.45)}
.pcard:focus-visible{outline:3px solid var(--ice-400);outline-offset:3px}
.pcard-ph{position:relative;display:block;aspect-ratio:3/2;overflow:hidden;background:var(--paper-100)}
.pcard-ph img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform .6s cubic-bezier(.2,.7,.3,1)}
.pcard:hover .pcard-ph img{transform:scale(1.05)}
/* The two chips sit on the photograph, so they carry their own ground. */
.pcard-kind,.pcard-pc{position:absolute;top:12px;font-size:11px;font-weight:800;
  letter-spacing:.07em;text-transform:uppercase;padding:6px 10px;border-radius:999px;
  line-height:1;backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.pcard-kind{left:12px;background:rgba(255,255,255,.94);color:var(--ink)}
.pcard-kind--ac{background:var(--ice-300);color:var(--ink)}
.pcard-kind--solar{background:#FFE08A;color:#4A3A00}
.pcard-pc{right:12px;background:rgba(10,17,25,.74);color:#fff}
.pcard-b{display:flex;flex-direction:column;gap:6px;padding:18px 20px 20px;flex:1}
.pcard-t{font-size:19px;font-weight:750;letter-spacing:-.015em;color:var(--ink)}
.pcard-s{font-size:13.5px;font-weight:650;color:var(--ice-700)}
/* Two lines of the note and no more, so every card in the row is the same
   height and the loop does not bob. The full sentence is on the project page. */
.pcard-n{font-size:14px;line-height:1.5;color:var(--ink-2);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.pcard-go{margin-top:auto;padding-top:10px;display:inline-flex;align-items:center;gap:7px;
  font-size:14px;font-weight:750;color:var(--ink)}
.pcard-go svg{width:15px;height:15px;color:var(--ice-600);transition:transform .2s ease}
.pcard:hover .pcard-go svg{transform:translateX(3px)}

@media (max-width:720px){
  .pcard{flex-basis:76vw}
  .proj-track,.proj-group{gap:14px}
  .proj-view{-webkit-mask-image:linear-gradient(90deg,transparent,#000 3%,#000 97%,transparent);
                    mask-image:linear-gradient(90deg,transparent,#000 3%,#000 97%,transparent)}
  .pcard-t{font-size:18px}
}
@media (prefers-reduced-motion:reduce){
  .pcard,.pcard-ph img,.pcard-go svg{transition:none}
}

/* ------------------------------------------------------------ step quizzes
   The heat pump and solar calculators, ported from David's dev-site design:
   one question at a time, large answer cards, a progress bar, the estimate
   revealed with the grant struck through. On white, centred, and no wider than
   a comfortable reading measure, because it is a conversation, not a table. */
.qz{max-width:780px;margin-inline:auto}
.qz-hd{text-align:center;margin-bottom:22px}
.qz-hd h2{margin:0}
.qz-hd .lede{margin:10px auto 0;max-width:56ch}
.qz-bar{height:6px;border-radius:999px;background:var(--paper-100);overflow:hidden;margin-bottom:20px}
.qz-bar i{display:block;height:100%;width:0;border-radius:inherit;
  background:linear-gradient(90deg,var(--ice-500),var(--ice-300));
  transition:width .45s cubic-bezier(.2,.7,.3,1)}
.qz-form{position:relative;background:#fff;border:1px solid var(--line);border-radius:24px;
  padding:clamp(22px,4vw,38px);box-shadow:0 1px 2px rgba(15,26,36,.04),0 22px 50px -30px rgba(15,26,36,.35)}
.qz-trap{position:absolute;left:-9999px;width:1px;height:1px;opacity:0}
.qz-step{border:0;margin:0;padding:0;min-width:0}
/* Without the script every step shows, as a plain form. With it, one. */
.qz.is-js .qz-step{display:none}
.qz.is-js .qz-step.is-on{display:block;animation:qzin .32s cubic-bezier(.2,.7,.3,1) both}
@keyframes qzin{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.qz:not(.is-js) .qz-step + .qz-step{margin-top:30px}
.qz:not(.is-js) .qz-result,.qz:not(.is-js) .qz-done,.qz:not(.is-js) .qz-nav{display:none}
.qz-q{display:block;font-size:clamp(21px,2.6vw,26px);font-weight:750;letter-spacing:-.02em;
  color:var(--ink);line-height:1.2;padding:0;outline:0}
.qz-num{display:block;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ice-700);margin-bottom:8px}
.qz-hint{margin:8px 0 0;color:var(--ink-2);font-size:15px}
.qz-opts{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;margin-top:20px}
.qz-opts--3{grid-template-columns:repeat(3,1fr)}
.qz-opt{position:relative;display:flex;flex-direction:column;justify-content:center;gap:3px;
  min-height:68px;padding:16px 46px 16px 18px;border:1.5px solid var(--line-2);border-radius:16px;
  background:#fff;cursor:pointer;transition:border-color .16s,background .16s,transform .16s,box-shadow .16s}
.qz-opt:hover{border-color:var(--ice-400);background:var(--ice-50)}
.qz-opt input{position:absolute;opacity:0;pointer-events:none}
.qz-opt-t{font-size:16.5px;font-weight:700;color:var(--ink)}
.qz-opt-n{font-size:13px;color:var(--ink-2)}
/* The tick, drawn in CSS: an empty ring that fills when chosen. */
.qz-opt::after{content:"";position:absolute;right:16px;top:50%;width:22px;height:22px;
  margin-top:-11px;border-radius:50%;border:2px solid var(--line-2);background:#fff;
  transition:background .16s,border-color .16s}
.qz-opt:has(input:checked){border-color:var(--ice-600);background:var(--ice-50);
  box-shadow:0 0 0 3px rgba(58,163,216,.18)}
.qz-opt:has(input:checked)::after{border-color:var(--ice-600);background:var(--ice-600)
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 8.2 6.8 11 12 5.5' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/14px no-repeat}
.qz-opt:has(input:focus-visible){outline:3px solid var(--ice-400);outline-offset:2px}
.qz-shake{animation:qzshake .38s ease}
@keyframes qzshake{20%,60%{transform:translateX(-6px)}40%,80%{transform:translateX(6px)}}

.qz-field{display:grid;gap:7px;margin-top:18px}
.qz-field span{font-size:13.5px;font-weight:700;color:var(--ink)}
.qz-field em{font-style:normal;font-weight:500;color:var(--ink-2)}
.qz-field input{min-height:54px;padding:0 16px;border:1.5px solid var(--line-2);border-radius:14px;
  font:inherit;font-size:16px;color:var(--ink);background:#fff}
.qz-field input:focus{outline:0;border-color:var(--ice-600);box-shadow:0 0 0 3px rgba(58,163,216,.2)}

.qz-nav{display:flex;justify-content:space-between;gap:12px;margin-top:26px}
.qz-nav [data-qz-next]{margin-left:auto;min-width:160px}
.qz-nav .btn--ghost-l{background:transparent;color:var(--ink);border:1.5px solid var(--line-2)}
.qz-nav .btn--ghost-l:hover{border-color:var(--ink);background:transparent}

/* The estimate. */
.qz-result{text-align:center}
.qz-est-l{margin:0;font-size:12.5px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--ice-700)}
.qz-was{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:8px 12px;
  margin-top:14px;font-size:15px;color:var(--ink-2)}
.qz-was s{color:var(--ink-2);font-weight:650}
.qz-save{background:#DDF6E6;color:#0E6B35;font-size:12.5px;font-weight:800;padding:6px 11px;border-radius:999px}
.qz-big{margin-top:10px;font-size:clamp(34px,6vw,54px);font-weight:800;letter-spacing:-.03em;
  color:var(--ink);line-height:1.05}
.qz-sub{margin:10px auto 0;max-width:52ch;font-weight:650;color:var(--ink)}
.qz-facts{list-style:none;margin:20px auto 0;padding:0;max-width:520px;display:grid;gap:8px;text-align:left}
.qz-facts li{position:relative;padding:11px 14px 11px 40px;border-radius:12px;background:var(--paper-50);
  font-size:14.5px;color:var(--ink)}
.qz-facts li::before{content:"";position:absolute;left:14px;top:50%;width:16px;height:16px;margin-top:-8px;
  border-radius:50%;background:var(--ice-600) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.5 8.2 7 10.6 11.5 5.8' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/11px no-repeat}
.qz-fine{margin:16px auto 0;max-width:60ch;font-size:13px;color:var(--ink-2)}
.qz-lock{margin-top:28px;padding-top:26px;border-top:1px solid var(--line);text-align:left}
.qz-lock h3{margin:0;font-size:22px}
.qz-lock > p{margin:6px 0 0;color:var(--ink-2);font-size:15px}
.qz-lock-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 14px}
.qz-field--wide{grid-column:1 / -1}
.qz-when{display:flex;flex-wrap:wrap;gap:8px;margin:18px 0 18px}
.qz-chip{position:relative;cursor:pointer}
.qz-chip input{position:absolute;opacity:0}
.qz-chip span{display:inline-flex;align-items:center;min-height:42px;padding:0 15px;border-radius:999px;
  border:1.5px solid var(--line-2);font-size:14px;font-weight:650;color:var(--ink);transition:.16s}
.qz-chip input:checked + span{border-color:var(--ice-600);background:var(--ice-50);color:var(--ice-700)}
.qz-chip input:focus-visible + span{outline:3px solid var(--ice-400);outline-offset:2px}
.qz-msg{margin:12px 0 0;font-size:14px;color:var(--ink-2)}
.qz-msg.is-err{color:#A1261B;font-weight:650}
.qz-alt{margin:12px 0 0;font-size:14px;color:var(--ink-2);text-align:center}
.qz-alt a{font-weight:750;color:var(--ice-700)}
.qz-done{text-align:center;padding:20px 0}
.qz-done-t{font-size:22px;font-weight:750;color:var(--ink);margin:0;outline:0}

@media (max-width:640px){
  .qz-opts,.qz-opts--3{grid-template-columns:1fr}
  .qz-lock-grid{grid-template-columns:1fr}
  .qz-form{border-radius:20px}
  .qz-nav [data-qz-next]{min-width:0;flex:1}
}
@media (prefers-reduced-motion:reduce){
  .qz.is-js .qz-step.is-on,.qz-shake{animation:none}
  .qz-bar i{transition:none}
}
/* A component class that sets display beats the browser's own [hidden] rule,
   because the user agent sheet loses to any author rule. .btn is inline-flex
   and .qz-was is flex, so without this the Back button showed on the first
   question and the struck-through before-grant line showed on a result that
   had no grant to strike through. */
.qz [hidden]{display:none!important}

/* A grid track of 1fr is minmax(auto, 1fr), and auto means "at least as wide
   as the widest thing that cannot wrap". A table or a long unbroken string
   inside the prose therefore pushed the column past the edge of a phone, and
   the whole page scrolled sideways: 394px on the repair page, 387px on
   contact, both at a 375px viewport. min-width:0 lets the column be the width
   of the screen and the table scroll inside its own wrapper, which is what
   .tbl-wrap is for. Found by the rendered audit, 11 September 2026. */
.split > *,.contact-grid > *,.price-grid > *,.hero-grid > *,.phero-grid > *{min-width:0}

/* The postcode in a district card title is the brand highlight, and ice-600 on
   white is 4.05:1. The card title is 19px bold, just under the large-text line
   on a phone, so it needs 4.5. */
.card h3 .hl{color:var(--ice-700)}

/* Same cause, one level down. .prose, .card and .cmethod are themselves grids,
   so their children were grid items with min-width:auto as well: a full-width
   button with a long nowrap label ("WhatsApp a photo of the error code")
   widened the repair page's side card to 394px, and the email address in the
   contact methods did the same to the contact page. Grid children may shrink;
   a full-width button may wrap onto two lines, because at full width it is the
   only thing on its row and wrapping costs nothing; an email address may break
   where it has to. */
.prose > *,.card > *,.cmethod > *,.sticky-side > *{min-width:0}
.btn--wide{white-space:normal;text-align:center}
.cmethod a,.prose a[href^="mailto:"]{overflow-wrap:anywhere}
/* The footer lockup is 6.5:1, so 48px tall is 314px wide, which a 320px phone
   with its page padding cannot hold. Width-led on small screens instead, so it
   scales down rather than squashing. */
@media (max-width:400px){ .logo--ftr{height:auto;width:min(314px,100%)} }

/* ==========================================================================
   SERVICE LANDING PAGES, 11 September 2026. See build/lib/landing.mjs.
   Modelled on the home page of the dev site David built: a light hero with
   the job photograph washed out behind it, the calculator or the form in a
   card on the right, then short sections that each say one thing.
   ========================================================================== */
.lp-hero{position:relative;overflow:clip;background:#fff;color:var(--ink);
  padding:calc(var(--nav-h) + 48px) 0 clamp(40px,5vw,72px);border-bottom:1px solid var(--line)}
.crumbs + .lp-hero{padding-top:clamp(28px,3.2vw,48px)}
.lp-hero-bg{position:absolute;top:0;right:0;bottom:0;width:64%;z-index:0}
.lp-hero-bg img{width:100%;height:100%;object-fit:cover;object-position:60% 45%}
.lp-hero-bg::after{content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,#fff 0%,rgba(255,255,255,.92) 25%,rgba(255,255,255,.62) 55%,rgba(255,255,255,.45) 100%)}
.lp-hero-grid{position:relative;z-index:1;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,520px);
  gap:clamp(28px,4vw,64px);align-items:center}
.lp-hero h1{color:var(--ink);font-size:clamp(36px,4vw,56px);line-height:1.05;letter-spacing:-.025em;max-width:17ch}
.lp-hero h1 .hl{color:var(--ice-600)}
.lp-lede{margin:18px 0 0;font-size:18px;line-height:1.55;color:var(--ink-2);max-width:48ch}
.lp-cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:26px}
.lp-cta .btn svg{width:16px;height:16px}
.lp-chips{list-style:none;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;
  margin:26px 0 0;padding:0;max-width:520px}
.lp-chips li{display:flex;flex-direction:column;gap:3px;padding:12px 15px;border-radius:14px;
  background:rgba(255,255,255,.94);border:1px solid var(--line);box-shadow:var(--sh-sm)}
.lp-chips b{font-family:var(--display);font-size:20px;font-weight:800;line-height:1.1;letter-spacing:-.02em;color:var(--ice-700)}
.lp-chips span{font-size:13.5px;line-height:1.35;color:var(--ink-2)}
.lp-hero-card{min-width:0}
.lp-ticks{margin-top:24px}
.lp-ticks li{color:var(--ink)}
.lp-ticks li::before{background:var(--ice-100)}
.lp-ticks li::after{border-color:var(--ice-700)}
.lp-ticks b{color:var(--ink)}
.lp-hero-card .quotecard{border:1px solid var(--line);box-shadow:var(--sh-lg)}

/* The calculator as a hero card: the same quiz, tighter. */
/* The whole card is white, heading included, so nothing in it sits on the
   photograph. The form inside loses its own card. */
.qz--card{max-width:none;margin:0;background:#fff;border:1px solid var(--line);border-radius:24px;
  padding:clamp(18px,2vw,26px);box-shadow:var(--sh-lg)}
.qz--card .qz-form{border:0;box-shadow:none;padding:0;background:none;border-radius:0}
.qz--card .qz-hd{text-align:left;margin-bottom:12px}
.qz--card .qz-hd h2{font-size:clamp(22px,2vw,26px)}
.qz--card .qz-hd .lede{margin:6px 0 0;font-size:14.5px;max-width:none}
.qz--card .qz-bar{margin-bottom:14px}
.qz--card .qz-q{font-size:clamp(19px,1.8vw,22px)}
.qz--card .qz-opts{gap:10px;margin-top:16px}
.qz--card .qz-opt{min-height:56px;padding:12px 42px 12px 15px}
.qz--card .qz-opt-t{font-size:15.5px}
.qz--card .qz-nav{margin-top:18px}

/* THE VIDEO VARIANT, 11 September 2026. Same grid, same card, dark ground:
   the loop runs the full width behind it under the home page's own veil, and
   the type goes white. On a phone the still and the portrait loop stay on,
   where the light variant drops its photograph, because here the picture is
   the background of the whole section rather than a wash on one side. */
.lp-hero--video{background:var(--ink-950);color:#fff;isolation:isolate;
  padding-top:calc(var(--nav-h) + 74px);border-bottom:0}
.lp-hero--video .lp-hero-bg{inset:0;width:100%;z-index:-2}
.lp-hero--video .lp-hero-bg::after{content:none}
.lp-hero--video .hero-veil{z-index:-1}
.lp-hero--video h1,.lp-hero--video .lp-hero-copy .lp-lede{color:#fff}
.lp-hero--video .lp-lede{color:#D2E2EE}
.lp-hero--video h1 .hl{color:var(--ice-300)}
.lp-hero--video .lp-ticks li{color:#fff}
.lp-hero--video .lp-ticks li::before{background:rgba(255,255,255,.16)}
.lp-hero--video .lp-ticks li::after{border-color:#fff}
.lp-hero--video .lp-ticks b{color:#fff}
.lp-hero--video .lp-chips li{background:rgba(9,17,25,.62);border-color:rgba(255,255,255,.22)}
.lp-hero--video .lp-chips b{color:var(--ice-300)}
.lp-hero--video .lp-chips span{color:#D2E2EE}

/* THE GRANT CARD. One bordered card, the offer on the left and the money on
   the right, which is the shape David's dev site closes with. Bordered and
   tinted rather than a full-width dark band: the rule on this site since
   11 September 2026 is that a card can carry a ground, a section cannot. */
.lp-offer-band{padding-block:clamp(26px,3.2vw,44px)}
.lp-offer{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,300px);gap:clamp(20px,3vw,44px);
  align-items:center;padding:clamp(22px,3vw,40px);border:1px solid var(--ice-200);
  border-radius:var(--r-panel);background:var(--ice-50)}
.lp-offer-k{margin:0 0 10px;font-size:12.5px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ice-700)}
.lp-offer h2{font-size:clamp(26px,2.8vw,38px);line-height:1.1;letter-spacing:-.02em;color:var(--ink)}
.lp-offer-t{margin:14px 0 0;font-size:16.5px;line-height:1.6;color:var(--ink-2);max-width:60ch}
.lp-offer .lp-ticks{margin-top:18px}
.lp-offer-cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:22px}
.lp-offer-fig{display:grid;gap:8px;justify-items:center;text-align:center;padding:clamp(18px,2vw,26px);
  background:#fff;border:1px solid var(--line);border-radius:18px;box-shadow:var(--sh-sm)}
.lp-offer-fig b{font-family:var(--display);font-size:clamp(44px,5vw,64px);font-weight:800;
  letter-spacing:-.03em;line-height:1;color:var(--ice-700)}
.lp-offer-fig span{font-size:14.5px;line-height:1.55;color:var(--ink-2)}

/* WHAT THEY BUY NEXT. Three photograph cards, the same shape as the projects
   cards so the page keeps one card language. */
/* auto-fit, because the heat pump page shows two of these and the solar page
   could show three: three fixed columns left a two-card row with a hole. */
.lp-rel{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr));gap:clamp(14px,1.8vw,22px);
  margin-top:clamp(20px,2.4vw,30px)}
.lp-rel-c{display:flex;flex-direction:column;overflow:hidden;text-decoration:none;
  border:1px solid var(--line);border-radius:var(--r-panel);background:#fff;
  transition:transform .18s ease,box-shadow .22s ease,border-color .18s ease}
.lp-rel-c:hover{transform:translateY(-3px);border-color:var(--ice-400);box-shadow:var(--sh-sm)}
.lp-rel-ph{display:block;aspect-ratio:3 / 2;overflow:hidden;background:var(--paper-100)}
.lp-rel-ph img{width:100%;height:100%;object-fit:cover}
.lp-rel-b{display:flex;flex-direction:column;gap:6px;padding:18px 20px 20px}
.lp-rel-price{font-size:12.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ice-700)}
.lp-rel-t{font-family:var(--display);font-size:21px;font-weight:750;letter-spacing:-.02em;color:var(--ink)}
.lp-rel-p{font-size:15px;line-height:1.55;color:var(--ink-2)}
.lp-rel-go{display:inline-flex;align-items:center;gap:7px;margin-top:6px;font-size:14.5px;
  font-weight:700;color:var(--ice-700)}
.lp-rel-go svg{width:15px;height:15px;transition:transform .2s ease}
.lp-rel-c:hover .lp-rel-go svg{transform:translateX(3px)}
@media (max-width:900px){
  .lp-offer{grid-template-columns:minmax(0,1fr)}
  .lp-rel{grid-template-columns:minmax(0,1fr)}
}

/* Real quotations as bars. The heading beside them on a laptop, above them on
   a phone; the figure sits on the bar in white, and the shortest bar is never
   drawn under 18 per cent so its figure always fits inside it. */
.lp-bars-grid{display:grid;grid-template-columns:minmax(0,340px) minmax(0,1fr);gap:clamp(22px,4vw,64px);align-items:start}
.lp-bars-hd h2{font-size:clamp(26px,2.8vw,38px);line-height:1.1;letter-spacing:-.02em}
.lp-bars-hd .lede{margin-top:12px}
.lp-bars-note{margin:14px 0 0;font-size:14px;line-height:1.55;color:var(--ink-2)}
.lp-bars{list-style:none;margin:0;padding:0;display:grid;gap:10px}
.lp-bars li{display:grid;grid-template-columns:minmax(0,150px) minmax(0,1fr);align-items:center;column-gap:14px;row-gap:2px}
.lp-bar-l{font-size:14px;font-weight:600;color:var(--ink-2);line-height:1.3}
.lp-bar-track{display:block;height:34px;border-radius:10px;background:var(--paper-50);overflow:hidden}
.lp-bar-fill{display:flex;align-items:center;justify-content:flex-end;height:100%;padding:0 12px;
  border-radius:10px;background:linear-gradient(90deg,var(--ice-500),var(--ice-700))}
.lp-bar-fill b{font-family:var(--display);font-size:15px;font-weight:800;color:#fff;white-space:nowrap;letter-spacing:-.01em}
.lp-bar-sub{grid-column:2;font-size:12.5px;color:var(--ink-2)}
/* Without a label the bar takes the whole row and the note sits beside it. */
.lp-bars li.lp-bar--nolabel{grid-template-columns:minmax(0,1fr) auto}
.lp-bar--nolabel .lp-bar-sub{grid-column:2;white-space:nowrap}
@media (prefers-reduced-motion:no-preference){
  .lp-bar-fill{animation:barIn .7s cubic-bezier(.2,.8,.3,1) both}
}
@keyframes barIn{from{transform:scaleX(.2);transform-origin:left;opacity:.2}to{transform:none;opacity:1}}
@media (max-width:900px){
  .lp-bars-grid{grid-template-columns:minmax(0,1fr)}
  .lp-bars li{grid-template-columns:minmax(0,1fr)}
  .lp-bar-sub{grid-column:1}
  .lp-bars li.lp-bar--nolabel{grid-template-columns:minmax(0,1fr) auto}
  .lp-bar--nolabel .lp-bar-sub{grid-column:2}
}

/* Link cards that are only a name and a line: two to a row on a phone and
   tighter, because seventeen of them one to a row was three and a half
   screens of cards on the areas page. */
@media (max-width:720px){
  .cards.cards--compact{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
  .cards--compact .card{padding:14px 14px 16px}
  .cards--compact .card h3{font-size:16px;line-height:1.25}
  .cards--compact .card p{font-size:13px;line-height:1.45;margin-top:4px}
}

/* THREE PICTURE CARDS ON A PHONE, as a row you swipe. Stacked, the home
   page's "three things people ask" and "three ways it goes in" were 2.0 and
   2.4 screens between them of one card after another. As a row, with the next
   card showing at the edge so it is obvious there is more, each is about one
   screen. Desktop is untouched. */
@media (max-width:720px){
  .qcards,.sys{display:flex;grid-template-columns:none;gap:12px;overflow-x:auto;overscroll-behavior-x:contain;
    scroll-snap-type:x mandatory;scrollbar-width:none;margin-inline:calc(var(--pad) * -1);padding:2px var(--pad) 6px;
    scroll-padding-inline:var(--pad)}
  .qcards::-webkit-scrollbar,.sys::-webkit-scrollbar{display:none}
  .qcards > *,.sys > *{flex:0 0 82%;scroll-snap-align:start}
  .sys img{aspect-ratio:4/3}
  .qcard img{aspect-ratio:16/10;object-fit:cover;width:100%;height:auto}
}

.district-more{max-width:760px;margin:18px auto 0}
.district-more .lp-more-in{padding-top:6px}

.lp-stats{background:var(--paper-50);border-bottom:1px solid var(--line)}
.lp-stats ul{list-style:none;margin:0;padding:clamp(22px,3vw,34px) 0;display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));gap:16px}
.lp-stats li{display:flex;flex-direction:column;gap:4px;text-align:center}
.lp-stats b{font-family:var(--display);font-size:clamp(28px,3vw,40px);font-weight:800;letter-spacing:-.03em;line-height:1;color:var(--ice-700)}
.lp-stats span{font-size:14.5px;color:var(--ink-2)}

.lp-steps{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px;counter-reset:none}
.lp-steps li{position:relative;display:grid;gap:10px;align-content:start;padding:26px 24px 24px;
  background:#fff;border:1px solid var(--line);border-radius:var(--r-panel);box-shadow:var(--sh-sm)}
.lp-step-n{position:absolute;top:18px;right:20px;font-family:var(--display);font-size:44px;font-weight:800;
  line-height:1;color:var(--ice-100)}
.lp-step-ic{width:48px;height:48px;border-radius:14px;display:grid;place-items:center;
  background:var(--ice-600);color:#fff;--mi-accent:#fff}
.lp-step-ic svg{width:26px;height:26px}
.lp-steps h3{font-size:19px;margin:6px 0 0}
.lp-steps p{margin:0;font-size:15.5px;line-height:1.55;color:var(--ink-2)}

.lp-inc{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}
.lp-inc li{display:flex;align-items:flex-start;gap:14px;padding:18px;background:#fff;
  border:1px solid var(--line);border-radius:18px}
.lp-inc-ic{flex:none;width:46px;height:46px;border-radius:13px;display:grid;place-items:center;
  background:var(--ice-100);color:var(--ice-800);--mi-accent:var(--ice-500)}
.lp-inc-ic svg{width:24px;height:24px}
.lp-inc-t{display:flex;flex-direction:column;gap:4px;min-width:0}
.lp-inc-t b{font-size:16px;font-weight:700;color:var(--ink)}
.lp-inc-t span{font-size:14.5px;line-height:1.5;color:var(--ink-2)}

/* The long guide, folded shut under one line. */
.lp-more-band{padding-block:clamp(28px,3.4vw,48px)}
.lp-more{border:1px solid var(--line);border-radius:var(--r-panel);background:#fff}
.lp-more > summary{display:flex;align-items:center;justify-content:space-between;gap:14px;cursor:pointer;
  list-style:none;padding:18px 22px;font-size:17px;font-weight:700;color:var(--ink)}
.lp-more > summary::-webkit-details-marker{display:none}
.lp-more > summary svg{flex:none;width:30px;height:30px;padding:10px 9px;border-radius:50%;
  background:var(--paper-100);color:var(--ink-2);transition:transform .25s ease}
.lp-more[open] > summary svg{transform:rotate(180deg)}
.lp-more > summary:focus-visible{outline:3px solid var(--ice-500);outline-offset:2px;border-radius:var(--r-panel)}
.lp-more-in{padding:0 22px 22px;border-top:1px solid var(--line)}
.lp-more-in > .wrap{width:auto;padding:0}
.lp-more-in > section.band{padding-block:clamp(20px,2.4vw,32px);background:none;border:0}
.lp-more-in > section.band + section.band{border-top:1px solid var(--line)}
.lp-more-in > section.band > .wrap{width:auto;padding:0}

@media (max-width:1000px){
  .lp-hero-grid{grid-template-columns:minmax(0,1fr);gap:26px}
  .lp-hero-bg{display:none}
  .lp-hero--video .lp-hero-bg{display:block}
  .lp-hero h1{max-width:none}
  .lp-steps,.lp-inc{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:720px){
  .lp-hero{padding-top:calc(var(--nav-h) + 28px)}
  .crumbs + .lp-hero{padding-top:22px}
  .lp-hero h1{font-size:clamp(32px,8.6vw,44px)}
  .lp-lede{font-size:16px;margin-top:12px}
  .lp-cta{margin-top:18px}
  .lp-cta .btn{flex:1 1 100%}
  .lp-chips{margin-top:18px;gap:8px}
  .lp-ticks{display:none}
  .lp-chips li{padding:10px 12px}
  .lp-chips b{font-size:17px}
  .lp-chips span{font-size:12.5px}
  .lp-stats ul{grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 10px}
  .lp-steps,.lp-inc{grid-template-columns:minmax(0,1fr)}
  .lp-steps li{padding:20px}
  .lp-more > summary{padding:16px 18px;font-size:16px}
  .lp-more-in{padding:0 16px 16px}
}
