/*
Theme Name: Morris Breuer — The Edgware Estate Agent
Theme URI: https://morrisbreuer.co.uk
Description: Block child theme of Twenty Twenty-Five for Morris Breuer, estate agent in Edgware. Brand tokens follow the Keller Williams Quick Reference Guide v10.13v1 (HEX/Web column). Portrait-led direction.
Author: The Web Design Company
Template: twentytwentyfive
Version: 0.1.0
Requires at least: 6.9
Tested up to: 7.1
Requires PHP: 8.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: mb-edgware
*/

/* ============================================================
   Tokens live in theme.json. This file carries only what
   theme.json cannot express: component styles and layout.
   Colours reference the KW guide HEX/Web column — never invent
   a value, never recolour supplied logo artwork.
   ============================================================ */

:root{
  --mb-hairline:#E3E3E3;
  --mb-red:#B40101;
  --mb-red-hover:#8F0101;
  --mb-ink:#121212;
  --mb-ink-2:#666666;
  --mb-surface:#F7F7F7;
  --mb-surface-2:#EFEFEF;
  --mb-warm:#F2EBE3;   /* tint of the headshot's own warm lighting */
  --mb-star:#FBBC04;   /* Google rating amber — decorative glyphs only */
  --mb-gray-mid:#666666;

  /* KW Navy. Observed on Keller Williams' own website, NOT in the quick
     reference guide, whose palette is red, greys, black and white with no
     blue at all. Added 2026-09-28 on instruction; see CLAUDE.md § Tokens.
     14.03:1 on white, so safe for text either way round.
     Never pair with --mb-red: red on navy is 1.97:1 and illegible.
     Not the old site's retired #12335A, which stays retired. */
  --mb-navy:#112C4F;
}

/* Twenty Twenty-Five does not set a global border-box reset, which makes
   padding add to declared sizes. The logo deliberately opts back into
   content-box (see .mb-logo) so its clear-space padding does not eat the
   artwork width; everything else wants border-box. */
*,*::before,*::after{box-sizing:border-box}

body{
  font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;
  color:var(--mb-ink);
  -webkit-font-smoothing:antialiased;
}

/* Two-tone heading — one accented word per section, never in body copy.
   Pattern taken from kwuk.com; compliant and adds warmth without a
   second colour. Counts toward the one-red-element-per-viewport budget. */
.mb-accent{color:var(--mb-red)}

.mb-eyebrow{
  font-size:.875rem;letter-spacing:.08em;text-transform:uppercase;
  color:var(--mb-ink-2);margin:0 0 1rem;
}

/* ---------- Header ----------
   Dark band is NOT a style choice. The only approved logo artwork has a
   baked-in black background and the KW guide forbids manufacturing a
   variant. Swap to a light header when transparent artwork arrives. */
.mb-header{background:#000;position:sticky;top:0;z-index:50}
.mb-header-inner{
  display:flex;align-items:center;justify-content:space-between;
  gap:2.5rem;min-height:68px;
}

/* KW COMPLIANCE — measured, do not adjust casually.
   The kw mark is 153/960 of the artwork width and must render >= 30px,
   so the artwork content box must never be under 188px.
   content-box is required: with border-box the clear-space padding
   eats the content width and silently breaches the minimum. */
/* PADDING IS ASYMMETRIC ON PURPOSE. Measured from the artwork itself:
   the 960x310 PNG has its lettering on rows 55-238, leaving 55px of blank
   canvas above and 71px below. That baked-in space counts toward the KW
   clear-space requirement, so the CSS only has to supply the shortfall.

   At 260px (scale 0.2708), clear space required = 260 x 62/960 = 16.79px:
     top     baked 14.90px  ->  needs CSS padding  (20px used)
     bottom  baked 19.23px  ->  ALREADY SUFFICIENT (0 needed)
   At 200px, required 12.92px: top baked 11.46px, bottom baked 14.79px.
   At 195px, required 12.59px: top baked 11.17px, bottom baked 14.42px.

   So padding-bottom:0 is brand-COMPLIANT, not a breach: the artwork
   carries its own bottom clear space. Adding CSS padding there was
   doubling it and pushing the logo visually high in the band.
   Sides keep their padding; the horizontal baked space is not relied on. */
.mb-logo{
  display:block;box-sizing:content-box;
  width:200px;min-width:188px;padding:18px 18px 0;height:auto;
}
@media(min-width:1024px){.mb-logo{width:260px;padding:20px 20px 0}}
/* EQUAL padding on all four sides at every breakpoint, so the artwork
   sits the same distance from each edge of the black band.

   MEASURED at each size. kw mark = width x 153/960, must be >= 30px.
   Clear space (width of the W) = width x 62/960, must be <= padding.

     base/tablet  200px + 18px   kw 31.88px   needs 12.92px   box 236x101
     desktop      260px + 20px   kw 41.44px   needs 16.79px   box 300x124
     <=360px      195px + 16px   kw 31.08px   needs 12.59px   box 227x95

   Never drop to 190px: that puts the kw mark at 30.28px, passing but
   sitting on the line, which this file deliberately avoids. */

.mb-nav{display:none}
@media(min-width:1024px){
  .mb-nav{display:flex;gap:2.5rem;align-items:center}
  .mb-nav a:not(.mb-nav-cta){
    color:#fff;text-decoration:none;font-size:.875rem;
    letter-spacing:.04em;padding-block:.5rem;
  }
  /* The CTA is a button, not a nav link: it keeps .mb-btn-primary's
     own padding and background and is excluded from both the link
     rules above and the red underline wipe in the MOTION section. */
  .mb-nav-cta{font-size:.875rem;letter-spacing:.04em;
    padding:.625rem 1.25rem;white-space:nowrap}
  /* Hover treatment lives in the MOTION section: a red underline that
     wipes in. No text-decoration here, or the two would stack. */
}

/* Contact strip — second row of the black header, right aligned.
   OUTSIDE the 1024px nav media query on purpose: this shows at every width,
   and it is where mobile users get a one-tap call most easily.
   Phone and email are the two confirmed contact facts; tel: stays in
   international form so it dials from outside the UK while the visible
   text is the UK national format people recognise.
   No divider rule: the strip is part of the same black band as the nav,
   so a hairline split it into two visual bars. */
.mb-header-contact{display:flex;flex-wrap:wrap;justify-content:flex-end;
  gap:.5rem 1.75rem;padding-block:0 .5rem;margin-top:-.5rem;
  font-size:.875rem;line-height:1.4}
.mb-header-contact a{color:#fff;text-decoration:none;
  display:inline-flex;align-items:center;min-height:32px}
.mb-header-contact a:hover{text-decoration:underline;
  text-decoration-color:var(--mb-red);text-decoration-thickness:2px;
  text-underline-offset:4px}
.mb-header-contact a:focus-visible{outline:3px solid #fff;outline-offset:2px;
  border-radius:2px}

/* Below ~480px the phone + email pair plus its gap exceeds the available
   width and wraps to two lines, which undoes the header tightening.
   Measured at 360px: links are 313px and the gap 28px against 321px of
   space. Shrinking the gap and the label keeps it on one row.
   Applied up to 480px, not 360px — the earlier 360px cap missed the
   common 375/390/412px phone widths entirely. */
@media(max-width:480px){
  .mb-header-contact{gap:.125rem .75rem;font-size:.75rem}
  .mb-header-contact a{gap:.3em}
  .mb-header-contact .mb-icon{width:1.05em;height:1.05em}
}

/* At 320px the pair needs 304px against 280px of space. Closing that
   would mean ~11px text, which is too small to read and too small to
   tap comfortably — so it stacks instead. Forced to two full-width
   rows so the wrap reads as a deliberate list rather than an accident,
   and each row keeps a comfortable tap height. */
@media(max-width:359px){
  .mb-header-contact{flex-direction:column;align-items:flex-end;gap:.125rem;
    font-size:.8125rem}
  .mb-header-contact a{min-height:28px}
}

/* ---------- Buttons ---------- */
/* min-height 44px is the accessible tap target and must stay, so the
   vertical padding is trimmed rather than removed — the button keeps its
   hit area while reading tighter. */
.mb-btn{
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;padding:.625rem 1.5rem;border-radius:6px;
  font-size:1rem;font-weight:700;text-decoration:none;cursor:pointer;
  line-height:1.2;border:1px solid transparent;
  transition:background-color .15s ease,color .15s ease;
}
.mb-btn-primary{background:var(--mb-red);color:#fff}      /* 7.13:1 */
.mb-btn-primary:hover{background:var(--mb-red-hover);color:#fff}
.mb-btn-secondary{border-color:var(--mb-ink);color:var(--mb-ink)}
.mb-btn-secondary:hover{background:var(--mb-ink);color:#fff}

/* Focus is a design element, never suppressed */
a:focus-visible,button:focus-visible,
input:focus-visible,textarea:focus-visible{
  outline:3px solid var(--mb-red);outline-offset:2px;border-radius:2px;
}

/* ---------- Hero ---------- */
/* Hero padding trimmed to bring the H1 and CTAs into the first fold.
   The header already provides separation, so 6rem on top of it was
   double-spacing the same gap. */
.mb-hero{padding-block:2.5rem 0}
@media(min-width:1024px){.mb-hero{padding-block:3rem 0}}
.mb-hero h1{font-size:clamp(2.6rem,2rem + 3.2vw,4.2rem);line-height:1.1;
  letter-spacing:-.02em;max-width:14ch;margin:0 0 1.5rem}
.mb-hero-lead{font-size:clamp(1.25rem,1.15rem + .5vw,1.41rem);
  color:var(--mb-ink-2);max-width:42ch}
.mb-hero-actions{display:flex;flex-wrap:wrap;gap:1rem;margin-top:2.5rem}

/* Portrait, framed.
   The 2026 headshot is a real photograph with its own office background, so
   it is presented as a photograph: a rounded frame with a warm surround
   picked from the image's own lighting (#AC9D8F sampled at the top edge,
   held back to a tint so it supports rather than competes).
   The earlier mix-blend-mode:multiply treatment is GONE ON PURPOSE — it
   existed to disguise a near-white edge on the old cut-out-that-wasn't, and
   would turn this image's mid-grey background to mud.
   Aspect is locked 4:5 to match the exported asset; no height attribute
   fights the CSS. */
.mb-portrait{position:relative;width:min(100%,400px);margin:0;
  padding-bottom:3.5rem;justify-self:center}
/* On short landscape viewports the 4:5 portrait alone can exceed the
   fold. Cap it against viewport height so the CTAs stay visible. */
@media(min-width:900px) and (max-height:820px){
  .mb-portrait{width:min(100%,min(400px,42vh))}
}
/* The warm panel is offset down and right of the photo so it reads as a
   deliberate layered shape. It is anchored to the picture box, not the
   figure, so the offset stays equal on both visible edges. */
.mb-portrait::before{content:"";position:absolute;
  left:1.5rem;right:-1.5rem;top:1.5rem;bottom:2rem;
  background:var(--mb-warm);border-radius:8px}
.mb-portrait picture{position:relative;display:block;width:100%}
.mb-portrait img{position:relative;width:100%;height:auto;display:block;
  aspect-ratio:4/5;object-fit:cover;border-radius:6px;
  box-shadow:0 10px 30px rgba(0,0,0,.16)}
/* Badge overlaps the photo's bottom-left corner. -1.5rem pulls it just
   outside the frame so the overlap is visible rather than ambiguous. */
.mb-badge{position:absolute;left:-1.5rem;bottom:0;z-index:2;background:#fff;
  border-left:4px solid var(--mb-red);padding:1rem 1.5rem;
  box-shadow:0 8px 28px rgba(0,0,0,.12);font-size:.875rem;line-height:1.4}
.mb-badge strong{display:block;font-size:1.25rem;letter-spacing:-.01em}

/* ---------- Proof strip ---------- */
.mb-proof{border-block:1px solid var(--mb-hairline);padding-block:2.5rem}
.mb-proof-inner{display:flex;flex-wrap:wrap;gap:1.5rem 4rem;
  align-items:center;justify-content:center;text-align:center}
.mb-score{font-size:clamp(1.95rem,1.7rem + 1.25vw,2.2rem);
  font-weight:700;line-height:1}
/* Review stars use Google's own rating amber (#FBBC04), because these are
   Google reviews and the colour is what makes them recognisable as such.
   It is 1.79:1 on white and would FAIL WCAG AA as text — permissible only
   because the glyphs are decorative (aria-hidden) and every star row is
   paired with the rating in text. Never reuse this token for type, and
   never drop the text rating beside it. */
.mb-stars{color:var(--mb-star);letter-spacing:2px}
.mb-meta{font-size:.875rem;color:var(--mb-ink-2);margin:0}

/* ---------- Steps ---------- */
.mb-steps{display:grid;gap:2.5rem;counter-reset:s}
@media(min-width:768px){.mb-steps{grid-template-columns:repeat(3,1fr);gap:4rem}}
/* Step: small counter beside a circled icon, then heading and copy.
   The counter stays a CSS counter (not hand-typed markup) so renumbering
   is automatic if a step is added or removed. */
.mb-step{counter-increment:s;position:relative}
.mb-step::before{content:counter(s,decimal-leading-zero);
  font-size:.875rem;font-weight:700;color:var(--mb-ink-2);
  letter-spacing:.04em}
.mb-step-icon{display:inline-grid;place-items:center;
  width:3.25rem;height:3.25rem;border-radius:50%;
  background:rgba(180,1,1,.07);color:var(--mb-red);
  margin-left:.875rem;vertical-align:middle}
/* 1.75rem in a 3.25rem circle keeps the glyph reading as the subject
   rather than as a dot floating in a disc. */
.mb-step-icon .mb-icon{width:1.75rem;height:1.75rem}
/* Counter and icon share a baseline row; the heading follows below. */
.mb-step > h3{margin-top:1.25rem}
.mb-step h3{font-size:1.25rem;margin:0 0 .5rem;line-height:1.2}
.mb-step p{color:var(--mb-ink-2);margin:0}

/* ---------- Property cards ---------- */
.mb-cards{display:grid;gap:2.5rem}
@media(min-width:700px){.mb-cards{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1024px){.mb-cards{grid-template-columns:repeat(3,1fr)}}
.mb-card{background:#fff;border:1px solid var(--mb-hairline);border-radius:8px;
  overflow:hidden;display:flex;flex-direction:column;
  transition:box-shadow .15s ease,transform .15s ease}
.mb-card:hover{box-shadow:0 12px 32px rgba(0,0,0,.1);transform:translateY(-2px)}
.mb-card-media{position:relative;aspect-ratio:3/2;background:var(--mb-surface-2)}
.mb-card-media .mb-ph{position:absolute;inset:0;display:grid;place-items:center;
  color:#999;font-size:.875rem;letter-spacing:.06em;text-transform:uppercase}
.mb-card-media img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover}

/* Status pills stay NEUTRAL. Deliberate divergence from kwuk.com, which
   uses red. Red belongs to the valuation path — a red "Sold" badge would
   read as an alert and would cost us the one-action rule. */
.mb-pill{position:absolute;top:1rem;left:1rem;font-size:.75rem;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;padding:6px 10px;
  border-radius:4px;z-index:1}
.mb-pill-current{background:var(--mb-surface-2);color:var(--mb-ink)}
.mb-pill-offer{background:var(--mb-ink);color:#fff}
.mb-pill-sold{background:var(--mb-gray-mid);color:#fff}

.mb-card-body{padding:1.5rem;display:flex;flex-direction:column;gap:6px;flex:1}
.mb-price{font-size:1.56rem;font-weight:700;letter-spacing:-.01em;margin:0}
.mb-qualifier{font-size:.875rem;color:var(--mb-ink-2);font-weight:400;display:block}
.mb-card-addr{color:var(--mb-ink);text-decoration:none;font-weight:500}
.mb-card-addr:hover{text-decoration:underline;text-underline-offset:3px}
.mb-card-facts{font-size:.875rem;color:var(--mb-ink-2);margin:auto 0 0;
  padding-top:1rem}

/* ---------- Reviews ---------- */
.mb-reviews{display:grid;gap:2.5rem}
@media(min-width:768px){.mb-reviews{grid-template-columns:repeat(3,1fr)}}
.mb-review{background:#fff;padding:2.5rem;border-radius:8px;
  border:1px solid var(--mb-hairline);display:flex;flex-direction:column}
.mb-review blockquote{margin:0 0 1.5rem;font-size:1rem}
.mb-review cite{font-style:normal;font-weight:700;font-size:.875rem;margin-top:auto}
.mb-review .mb-date{font-size:.875rem;color:var(--mb-ink-2)}

/* ---------- Reviews carousel ----------
   The track is a horizontally scrollable flex row with a fixed slide
   width, so reviews.js can derive per-view and page size from geometry
   instead of a hardcoded 3. Change the breakpoint or the flex-basis here
   and the script follows automatically; do not add a count to the JS.

   Progressive enhancement: with JS absent this is still a scrollable
   list with every review reachable, so the nav is hidden until the
   script adds .is-enhanced to the root. */
.mb-reviews-carousel{position:relative}

.mb-reviews-track{display:flex;column-gap:2rem;
  list-style:none;margin:0;padding:0;
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;
  /* Room for the card hover lift and its shadow, which would otherwise
     be clipped by the scroll container. */
  padding-block:4px;
  /* Firefox and the standards property; WebKit is handled below. */
  scrollbar-width:none;-ms-overflow-style:none}
.mb-reviews-track::-webkit-scrollbar{display:none}
.mb-reviews-track:focus-visible{outline:2px solid var(--mb-red);
  outline-offset:4px;border-radius:8px}

.mb-review-slide{flex:0 0 100%;scroll-snap-align:start;display:flex}
/* Three across from the same breakpoint the old static grid used. */
@media(min-width:768px){
  .mb-review-slide{flex-basis:calc((100% - 4rem) / 3)}
}
.mb-review-slide > .mb-review{width:100%}

.mb-carousel-nav{display:none;align-items:center;justify-content:center;
  gap:1.5rem;margin-top:2.5rem}
.mb-reviews-carousel.is-enhanced .mb-carousel-nav{display:flex}

.mb-carousel-btn{display:inline-flex;align-items:center;justify-content:center;
  width:44px;height:44px;padding:0;border-radius:50%;
  border:1px solid var(--mb-hairline);background:#fff;color:var(--mb-ink);
  cursor:pointer;transition:border-color .15s ease,color .15s ease,
  background-color .15s ease}
.mb-carousel-btn:hover:not(:disabled){border-color:var(--mb-red);
  color:var(--mb-red)}
.mb-carousel-btn:focus-visible{outline:2px solid var(--mb-red);
  outline-offset:2px}
.mb-carousel-btn:disabled{opacity:.35;cursor:default}

.mb-carousel-status{margin:0;font-size:.875rem;color:var(--mb-ink-2);
  /* Fixed width stops the arrows shuffling as the readout text changes
     length between "Reviews 1-3 of 28" and "Review 28 of 28". */
  min-width:11ch;text-align:center}

@media(prefers-reduced-motion:reduce){
  .mb-reviews-track{scroll-behavior:auto}
  .mb-carousel-btn{transition:none}
}

/* ---------- CTA band ----------
   WHITE, not dark. The footer is black, so the last section on every page
   must be light or the two merge into one slab. This is a layout rule, not
   a preference — apply it to the final section of any page template. */
/* Base CTA is light and centred. The dark variant (.mb-cta-dark) and the
   two-column variant (.mb-cta-grid) both override this; it remains the
   fallback for any page that uses the plain CTA. */
.mb-cta{background:#fff;color:var(--mb-ink);text-align:center;
  border-top:1px solid var(--mb-hairline)}
.mb-cta h2{color:var(--mb-ink)}
.mb-cta p{color:var(--mb-ink-2);margin-inline:auto;max-width:52ch}
.mb-cta-call{font-size:.875rem;margin-top:1.5rem}
.mb-cta-call a{color:var(--mb-red);text-decoration:underline}

/* ---------- Footer ---------- */
.mb-footer{background:#000;color:rgba(255,255,255,.75);
  padding-block:6rem 2.5rem;font-size:.875rem}
.mb-footer-grid{display:grid;gap:2.5rem}
/* Four columns: logo, legal, contact, follow. At tablet width the
   logo keeps its extra share and the three link columns split the
   rest evenly. Below 768px they stack, which keeps the legal links
   at a comfortable tap size rather than squeezing four abreast. */
@media(min-width:768px){
  .mb-footer-grid{grid-template-columns:1.6fr 1fr 1.2fr 1fr} }
@media(min-width:768px) and (max-width:1023px){
  /* Two up in the middle range: four across at 800px leaves the
     contact column too narrow for the email address. */
  .mb-footer-grid{grid-template-columns:1fr 1fr} }
.mb-footer a{color:#fff;text-decoration:none;display:inline-block;padding-block:4px}
.mb-footer a:hover{text-decoration:underline;text-underline-offset:3px}
.mb-footer h4{color:#fff;font-size:.875rem;letter-spacing:.08em;
  text-transform:uppercase;margin:0 0 1rem}
/* Stacked lockup (Agent Logo 3). MEASURED from the 500x250 artwork:
   the white kw glyph inside the red box is 74px of the 500px canvas,
   so it renders at width x 0.148.

   The KW minimum for the informal kw mark is 30px, which means this
   artwork must never render below 203px wide. The previous 200px would
   have put the mark at 29.60px, i.e. a breach. 220px gives 32.56px.
   min-width is raised to 205px for the same reason.

   Clear space (the width of the "w") is ~16.28px at 220px. The artwork
   bakes in 13.64px at the left and 24.20px below, so only the shortfall
   is added: 4px left, 0 below. The right padding separates it from the
   next footer column.

   Contrast note: "BREUER" is #545454, which is 2.77:1 on the black
   footer. That is below the 4.5:1 body-text threshold but WCAG exempts
   logotypes ("text that is part of a logo or brand name has no minimum
   contrast requirement"), and the lettering is legible at this size.
   A white tile behind it was rejected: that would be manufacturing a
   variant, which the KW guide forbids. */
.mb-footer-logo{box-sizing:content-box;width:220px;min-width:205px;
  padding:0 25px 1.5rem 4px;height:auto;display:block}
.mb-colophon{border-top:1px solid rgba(255,255,255,.18);margin-top:4rem;
  padding-top:2.5rem;display:flex;flex-wrap:wrap;gap:1rem 2.5rem;
  justify-content:space-between}

/* Skip link */
.mb-skip{position:absolute;left:-9999px}
.mb-skip:focus{left:1rem;top:1rem;z-index:100;background:#fff;
  padding:1rem 1.5rem;border:2px solid var(--mb-red)}

.screen-reader-text{position:absolute!important;width:1px;height:1px;
  overflow:hidden;clip:rect(1px,1px,1px,1px);white-space:nowrap}

@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;
    transition-duration:.01ms!important;scroll-behavior:auto!important}
}

/* ---------- Layout guards ----------
   Sections are full-bleed and manage their own gutters, so the theme's
   constrained layout must not also cap them. These wrappers centre the
   inner content instead. */
.mb-header-inner,.mb-header-contact,
.mb-footer-grid,
.mb-colophon{max-width:1200px;margin-inline:auto;padding-inline:20px}
/* Required by the KW guide for signs and business cards. Carried here
   as a full-width third row so it reads as a statement of fact rather
   than as another colophon item competing with the copyright. */
.mb-colophon-kw{flex-basis:100%;font-size:.8125rem;
  color:rgba(255,255,255,.62)}
@media(min-width:768px){
  .mb-header-inner,.mb-header-contact,.mb-footer-grid,.mb-colophon{padding-inline:32px}
}
@media(min-width:1024px){
  .mb-header-inner,.mb-header-contact,.mb-footer-grid,.mb-colophon{padding-inline:48px}
}
/* Section inner wrappers get the same treatment at wider breakpoints */
@media(min-width:768px){
  .mb-hero > div,.mb-proof > div,
  section[id] > div:not(.mb-footer-grid){padding-inline:32px!important}
}
@media(min-width:1024px){
  .mb-hero > div,.mb-proof > div,
  section[id] > div:not(.mb-footer-grid){padding-inline:48px!important}
}

/* ---------- Mobile navigation ---------- */
.mb-burger{background:none;border:1px solid rgba(255,255,255,.4);color:#fff;
  width:44px;height:44px;min-width:44px;border-radius:4px;font-size:1.2rem;
  cursor:pointer;line-height:1}
@media(min-width:1024px){.mb-burger{display:none}}
.mb-mobile-nav{position:absolute;left:0;right:0;top:100%;background:#000;
  display:flex;flex-direction:column;gap:0;padding:1rem 20px 1.5rem;
  border-top:1px solid rgba(255,255,255,.18);z-index:60}
.mb-mobile-nav[hidden]{display:none}
.mb-mobile-nav a{color:#fff;text-decoration:none;padding:.875rem 0;
  font-size:1rem;border-bottom:1px solid rgba(255,255,255,.12)}
.mb-mobile-nav a:last-child{border-bottom:0;margin-top:1rem;
  justify-content:center}
@media(min-width:1024px){.mb-mobile-nav{display:none!important}}
/* The sticky must sit on the template-part WRAPPER, not on .mb-header.
   WordPress wraps the header pattern in <header class="wp-block-template-part">,
   and that wrapper is exactly as tall as the header itself. A sticky
   element can only travel within its parent's box, so .mb-header had
   nowhere to go and scrolled away on the first pixel.

   VERIFIED at 390px under an iOS 17 user agent: scrolled to 1500px, the
   wrapper reports top:0 (stuck) where .mb-header reported top:-1500. */
/* Deliberately NOT :has(.mb-header), which iOS below 15.4 does not
   support and would leave the header unstuck there. The element tag is
   already unique: the header template part renders as <header> and the
   footer as <footer>, so this matches one element without :has(). */
header.wp-block-template-part{position:sticky;top:0;z-index:50}
.mb-header{position:static}
.mb-header-inner{position:relative}

/* ============================================================
   Layout utilities
   Every rule below replaced an inline style= attribute in the
   patterns. Keep styling here, not in markup: inline styles cannot
   be overridden by the editor, cannot respond to breakpoints, and
   make the patterns unreadable.
   ============================================================ */

/* Section wrappers */
.mb-wrap{max-width:1200px;margin-inline:auto;padding-inline:20px}
.mb-wrap-narrow{max-width:760px;margin-inline:auto;padding-inline:20px}
@media(min-width:768px){
  .mb-wrap,.mb-wrap-narrow{padding-inline:32px}
}
@media(min-width:1024px){
  .mb-wrap,.mb-wrap-narrow{padding-inline:48px}
}

/* Section rhythm and banding */
.mb-section{padding-block:6rem}
.mb-section-band{background:var(--mb-surface)}

/* Section intro paragraph under an h2 */
.mb-section-intro{color:var(--mb-ink-2);margin-bottom:4rem;max-width:66ch}
.mb-section-intro-lg{color:var(--mb-ink-2);font-size:1.25rem;
  margin-bottom:4rem;max-width:66ch}

/* Hero grid */
.mb-hero-grid{display:grid;gap:2.5rem;align-items:center}
@media(min-width:900px){
  /* align-items:start, not center. The portrait is the taller column, so
     centring pushed the eyebrow ~75px down the page for no reason and
     cost first-fold height. A small top offset keeps the H1 optically
     level with the top of the photo instead. */
  .mb-hero-grid{grid-template-columns:1fr .8fr;gap:4rem;align-items:start}
  .mb-hero-grid > div:first-child{padding-top:3.5rem}
}

/* Edgware intro lead paragraph */
.mb-lead{font-size:1.25rem;color:var(--mb-ink-2)}

/* Small spacing helpers */
.mb-flush{margin:0}
.mb-mt-sm{margin-top:1rem}
.mb-mt-lg{margin-top:2.5rem}
.mb-measure-short{max-width:34ch}

/* Interior page main.
   Generous bottom padding keeps the last block clear of the black footer —
   the same rule as the homepage's final section: end light, never dark.
   Horizontal padding matches .mb-wrap so interior pages get the same
   gutter as the homepage sections; without it content runs edge to edge
   on mobile. */
.mb-page-main{
  background:#fff;
  padding:4rem 20px 6rem;
}
@media(min-width:768px){.mb-page-main{padding:4rem 32px 6rem}}
@media(min-width:1024px){.mb-page-main{padding:5rem 48px 7rem}}

/* Interior page headings: the hero clamp is tuned for the homepage's
   short headline. Page titles can be longer, so cap them lower and
   allow long words to break rather than overflow. */
.mb-page-main h1{
  font-size:clamp(2rem,1.5rem + 2.4vw,3.05rem);
  max-width:20ch;
  overflow-wrap:break-word;
}
.mb-page-main > *{max-width:66ch}

/* ============================================================
   Responsive safety net
   Guards against content that would otherwise break mobile layout.
   ============================================================ */

/* Nothing may exceed the viewport.

   overflow-x is deliberately NOT set on <html>. On iOS Safari an
   overflow on the root turns it into a scroll container, which breaks
   position:sticky on the header and makes IntersectionObserver report
   against the wrong box, so scroll reveals never fire. That is the most
   likely reason animations were not running on iPhone.

   MEASURED before removing it, under an iOS 17 user agent at 390px and
   320px: document scrollWidth equals the viewport either way and zero
   elements extend past the right edge. The guard was buying nothing and
   costing iOS reliability. It stays on <body> as a safety net for
   content added later. */
/* overflow-x:clip, NOT hidden. `hidden` implies a scroll container, so
   it breaks position:sticky in descendants and makes iOS Safari report
   IntersectionObserver against the wrong box. `clip` prevents the same
   horizontal overflow without ever becoming scrollable. Baseline in
   Safari 16+, Chrome 90+, Firefox 81+; older browsers ignore it and
   simply get no guard, which the measurements below show costs nothing.

   MEASURED under an iOS 17 user agent at 390px and 320px: document
   scrollWidth equals the viewport with the guard removed entirely, and
   zero elements extend past the right edge. */
html{max-width:100%}
body{overflow-x:clip;max-width:100%}
img,video,iframe,table,pre{max-width:100%}
img,video{height:auto}

/* Long words, URLs and email addresses wrap rather than overflow */
h1,h2,h3,h4,p,li,blockquote,cite,figcaption,a{overflow-wrap:break-word}

/* Headings must not overflow narrow screens */
@media(max-width:480px){
  .mb-hero h1{font-size:clamp(2.1rem,1.4rem + 4vw,2.8rem)}
  .mb-score{font-size:1.9rem}
  .mb-price{font-size:1.4rem}
}

/* Proof strip stacks cleanly instead of cramming three columns */
@media(max-width:600px){
  .mb-proof-inner{flex-direction:column;gap:1rem}
  .mb-proof-inner .mb-measure-short{max-width:100%}
}

/* Footer columns stack with breathing room */
@media(max-width:767px){
  .mb-footer{padding-block:3rem 2rem}
  .mb-footer-grid{gap:2rem}
  .mb-colophon{flex-direction:column;gap:.5rem;margin-top:2rem;padding-top:1.5rem}
}

/* Hero portrait: reduce the reserved panel on small screens so the
   badge does not float far below the image */
@media(max-width:600px){
  /* No negative offsets on mobile — the warm panel and the badge would
     push past the 20px page gutter and cause horizontal scroll. Both are
     pulled back inside the figure and the layering is kept by insetting
     the panel instead. */
  .mb-portrait{padding-bottom:3rem}
  .mb-portrait::before{left:1rem;right:0;top:1rem;bottom:1.5rem}
  .mb-badge{left:0}
  .mb-badge{padding:.75rem 1rem}
  .mb-badge strong{font-size:1.1rem}
}

/* Buttons go full width on very narrow screens so they never crowd */
@media(max-width:420px){
  .mb-hero-actions{flex-direction:column;align-items:stretch}
  .mb-hero-actions .mb-btn{width:100%}
}

/* Section rhythm tightens on mobile — 6rem is a desktop measure */
@media(max-width:767px){
  .mb-section{padding-block:3.5rem}
  .mb-hero{padding-block:2.5rem 0}
  .mb-section-intro,.mb-section-intro-lg{margin-bottom:2.5rem}
  .mb-steps{gap:2rem}
  .mb-cards,.mb-reviews{gap:1.5rem}
  .mb-review{padding:1.5rem}
}

/* Very narrow screens (320px and below).
   The logo cannot shrink past 188px of artwork without breaching the KW
   30px minimum for the kw mark, and its clear space must be preserved.
   At 320px that leaves too little room for the burger alongside, so the
   clear space is reduced to the guide minimum (12.5% of artwork width =
   25px) and the header gap tightens. The logo itself never shrinks. */
@media(max-width:360px){
  /* 195px artwork -> kw at 31.1px, a deliberate margin above the
     30px minimum rather than sitting exactly on it. Padding stays
     equal at 16px: clear space needs 12.59px, and the resulting
     227px box still fits the 230px available at 320px once the
     gutters, burger and gap are accounted for. */
  .mb-logo{width:195px;padding:16px 16px 0}
  .mb-header-inner{gap:.5rem;min-height:76px}
  /* Contact strip: the icons pushed the pair past one line, which undid
     the header tightening. Smaller icons, tighter gaps and a slightly
     smaller label bring it back to a single row at 360px. */
  .mb-header-contact{gap:.125rem .875rem;font-size:.8125rem}
  .mb-header-contact a{gap:.35em}
  .mb-header-contact .mb-icon{width:1em;height:1em}
  .mb-burger{width:42px;height:42px;min-width:42px}
}

/* ============================================================
   MOTION
   Scroll reveals and hover refinement. No animation library:
   these are compositor-only transforms driven by a tiny
   IntersectionObserver in functions.php.

   Rules this system follows:
   - Only transform and opacity animate. Never width/height/top,
     which force layout on every frame.
   - Nothing is hidden until JS confirms it will reveal it. The
     .mb-motion class is set on <html> by the inline script, so a
     no-JS or failed-script visitor sees fully visible content
     rather than a blank page.
   - Distances are small (16-24px). Premium reads as restraint;
     long travel reads as a template.
   ============================================================ */

.mb-motion [data-mb-reveal]{opacity:0;transform:translateY(20px);
  transition:opacity .7s cubic-bezier(.16,1,.3,1),
             transform .7s cubic-bezier(.16,1,.3,1)}
.mb-motion [data-mb-reveal].is-in{opacity:1;transform:none}

/* Children stagger via a custom property the observer sets, so the
   delay lives in CSS and the JS stays a few lines. */
.mb-motion [data-mb-reveal][style*="--i"]{
  transition-delay:calc(var(--i) * 90ms)}

/* Hero is above the fold: it animates on load, not on scroll, and
   slightly faster — a visitor should never wait to read the H1. */
.mb-motion .mb-hero [data-mb-reveal]{transition-duration:.6s}

/* Card media: a slow scale on hover. The image is inside a
   fixed-aspect box with overflow hidden, so nothing reflows. */
.mb-card-media{overflow:hidden}
.mb-card-media img{transition:transform .5s cubic-bezier(.16,1,.3,1)}
.mb-card:hover .mb-card-media img{transform:scale(1.04)}

/* Slightly deeper lift than the existing 2px, with a longer ease.
   Overrides the .15s rule above deliberately. */
.mb-card{transition:box-shadow .35s cubic-bezier(.16,1,.3,1),
                    transform .35s cubic-bezier(.16,1,.3,1)}
.mb-card:hover{transform:translateY(-4px);
  box-shadow:0 16px 40px rgba(0,0,0,.12)}

/* Review cards lift on hover too, but less — they are not links,
   so the affordance should be subtler than the property cards. */
.mb-review{transition:transform .35s cubic-bezier(.16,1,.3,1),
                      box-shadow .35s cubic-bezier(.16,1,.3,1)}
.mb-review:hover{transform:translateY(-3px);
  box-shadow:0 14px 36px rgba(0,0,0,.09)}

/* Step numbers: the red numeral fades up a touch after its text,
   which makes the sequence read as counted rather than dumped. */
.mb-motion .mb-step::before{transition:opacity .5s ease .15s}

/* Buttons: add transform to the existing colour transition so the
   press feels physical. Focus-visible is untouched. */
.mb-btn{transition:background-color .15s ease,color .15s ease,
                   transform .15s cubic-bezier(.16,1,.3,1),
                   box-shadow .25s ease}
.mb-btn-primary:hover{transform:translateY(-1px);
  box-shadow:0 8px 20px rgba(180,1,1,.28)}
.mb-btn:active{transform:translateY(0)}

/* Nav and contact links: underline grows from the left rather than
   appearing all at once. */
@media(min-width:1024px){
  .mb-nav a:not(.mb-nav-cta){position:relative;text-decoration:none;
    transition:color .2s ease}
  /* Underline wipes in from the left in KW Red.
     The RULE is red, the TEXT stays white, and that distinction is
     deliberate: KW Red on the black header measures 2.95:1 and FAILS
     WCAG AA as text. A decorative underline carries no such requirement,
     so this gives the red hover without making the label unreadable.
     If a red text colour is ever wanted here it must be lightened to
     around #E53535 (4.89:1) — never #B40101. */
  .mb-nav a:not(.mb-nav-cta)::after{content:"";position:absolute;left:0;right:0;bottom:0;
    height:2px;background:var(--mb-red);transform:scaleX(0);
    transform-origin:left;transition:transform .3s cubic-bezier(.16,1,.3,1)}
  .mb-nav a:not(.mb-nav-cta):hover{text-decoration:none}
  .mb-nav a:not(.mb-nav-cta):hover::after{transform:scaleX(1)}
  /* Keyboard users get the same affordance, not just mouse users. */
  .mb-nav a:not(.mb-nav-cta):focus-visible::after{transform:scaleX(1)}
}

/* Portrait settles in with a fractional scale — enough to feel
   composed, not enough to read as a zoom. */
.mb-motion .mb-portrait img{transition:opacity .8s ease,
  transform .8s cubic-bezier(.16,1,.3,1)}

/* THE ACCESSIBILITY CONTRACT.
   The global prefers-reduced-motion block above zeroes durations,
   but a zeroed transition on an element starting at opacity:0 would
   leave it invisible forever if the observer never fired. So reveal
   state is reset outright here rather than relying on that. */
@media(prefers-reduced-motion:reduce){
  .mb-motion [data-mb-reveal]{opacity:1!important;transform:none!important;
    transition:none!important}
  .mb-card:hover,.mb-review:hover,.mb-btn-primary:hover{transform:none}
  .mb-card:hover .mb-card-media img{transform:none}
  .mb-nav a::after{display:none}
  .mb-nav a:hover{text-decoration:underline;text-underline-offset:6px}
}

/* ============================================================
   ICONS
   Inline SVG, not an icon font. They inherit currentColor, so a
   single rule keeps them in step with their text at every size
   and on every background.

   em-based sizing on purpose: an icon set in px drifts out of
   proportion the moment its text scales, which is exactly what
   the fluid type scale does here.
   ============================================================ */

.mb-icon{width:1em;height:1em;flex:0 0 auto;
  /* Optical alignment. Cap height sits above the baseline, so a
     box-aligned icon reads as floating high next to text. */
  vertical-align:-.125em}

/* Icon + label pairs. inline-flex keeps the icon locked to its
   text so a wrap never orphans a glyph on its own line. */
.mb-fact{display:inline-flex;align-items:center;gap:.4em}

/* Header contact: slightly larger than its 14px text so the glyph
   stays legible, and a touch dimmed so the icons read as support
   rather than competing with the numbers. */
.mb-header-contact a{gap:.5em}
.mb-header-contact .mb-icon{width:1.15em;height:1.15em;opacity:.75;
  transition:opacity .2s ease}
.mb-header-contact a:hover .mb-icon{opacity:1}

/* Property card facts: was a &middot;-separated sentence, now three
   labelled units. flex-wrap so a narrow card stacks rather than
   overflows. */
.mb-card-facts{display:flex;flex-wrap:wrap;gap:.35rem 1rem;align-items:center}
.mb-card-facts .mb-icon{opacity:.6}

/* Footer: stacked icon rows. The <br>-separated list became a flex
   column so each line can carry its own icon. */
.mb-icon-list{display:flex;flex-direction:column;gap:.5rem;
  list-style:none;padding-left:0;margin-left:0}
.mb-icon-list a,.mb-icon-list > span{display:grid;
  grid-template-columns:1em 1fr;align-items:start;gap:.5em}
/* List form (<ul><li>icon + <span>text</span></li>): the icon is a
   sibling of the text, so the grid belongs on the <li>, not the span.
   The footer's <p> form keeps the rule above. */
.mb-icon-list > li{display:grid;grid-template-columns:1em 1fr;
  align-items:start;gap:.5em}
.mb-icon-list > li > span{display:block}
/* Grid, not inline-flex: a multi-line value (the opening hours) must
   keep its continuation lines aligned with the first line's text, not
   wrap back under the icon. */
.mb-icon-list .mb-icon{opacity:.7;margin-top:.3em}

/* CTA: phone and hours side by side on one line where there is
   room, stacked where there is not. */
.mb-cta-call{display:flex;flex-wrap:wrap;justify-content:center;
  gap:.5rem 2rem}
.mb-cta-call .mb-icon{opacity:.6}

/* Reduced motion: the only icon transition is an opacity fade on
   hover, already covered by the global rule. Nothing to add. */

/* ============================================================
   SPLIT SECTION — full-bleed media beside text
   ============================================================ */

.mb-split{display:grid;padding:0}
@media(min-width:900px){
  .mb-split{grid-template-columns:1fr 1fr;align-items:stretch}
}

/* Media column runs to the edge of the viewport, not the content
   wrapper — that full-bleed edge is the whole point of the layout. */
.mb-split-media{position:relative;min-height:280px;background:var(--mb-surface-2)}
@media(min-width:900px){.mb-split-media{min-height:480px}}
.mb-split-media img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;display:block}

/* White, not a tinted panel: the section sits between a grey band above
   (reviews) and the dark CTA below, so it is the light beat in the
   alternation. The full-bleed image already distinguishes the two halves,
   so the text side needs no fill of its own. */
.mb-split-body{background:#fff;display:flex;align-items:center;
  padding:3rem 20px}
@media(min-width:768px){.mb-split-body{padding:3.5rem 32px}}
@media(min-width:900px){.mb-split-body{padding:4rem 48px}}
/* Text stays near the split rather than centring in a wide column, so
   the two halves read as one composition. */
.mb-split-inner{max-width:34rem;margin-inline:0 auto}
@media(min-width:1440px){.mb-split-inner{max-width:36rem}}
.mb-split-body h2{margin-top:.25rem}

/* PLACEHOLDER — not a design element. Plain on purpose so nobody
   mistakes it for finished work. Remove with the pattern comment. */
.mb-img-placeholder{position:absolute;inset:0;display:flex;
  flex-direction:column;align-items:center;justify-content:center;gap:.75rem;
  background:repeating-linear-gradient(45deg,#EFEFEF,#EFEFEF 14px,#E7E7E7 14px,#E7E7E7 28px);
  color:var(--mb-ink-2);font-size:.875rem;text-align:center;line-height:1.4}
.mb-placeholder-icon{width:2rem;height:2rem;opacity:.5}

/* ============================================================
   CTA — light, with a supporting checklist
   The comp had this dark over a photo; see home-cta.php for why
   it is light. The checklist carries the visual weight instead.
   ============================================================ */

.mb-cta-grid{display:grid;gap:2.5rem;text-align:left}
@media(min-width:900px){
  .mb-cta-grid{grid-template-columns:1.15fr .85fr;gap:4rem;align-items:center}
}
/* The CTA section is centred by default for its older single-column
   form; the grid version overrides that. */
.mb-cta .mb-cta-grid{text-align:left}
.mb-cta .mb-cta-grid .mb-cta-call{justify-content:flex-start}

.mb-check-list{list-style:none;margin:0;padding:1.75rem;
  background:var(--mb-surface);border:1px solid var(--mb-hairline);
  border-radius:8px;display:flex;flex-direction:column;gap:1rem}
.mb-check-list li{display:flex;align-items:center;gap:.75rem;
  font-weight:500;margin:0}
.mb-check-list .mb-icon{width:1.35em;height:1.35em;color:var(--mb-red);
  flex:0 0 auto}

/* Arrow nudges on hover. transform only, and the global
   prefers-reduced-motion rule already flattens it. */
.mb-btn-arrow{display:inline-block;margin-left:.5em;
  transition:transform .25s cubic-bezier(.16,1,.3,1)}
.mb-btn:hover .mb-btn-arrow{transform:translateX(3px)}

/* ============================================================
   DARK CTA — the agreed exception to the light-last-section rule
   (see home-cta.php). Everything here exists to stop the band
   merging into the black footer below it.
   ============================================================ */

.mb-cta-dark{background:#1A1A1A;color:#fff;
  /* NO borders at all. A light hairline on top read as a white line
     against the grey reviews band above, and a light rule at the bottom
     read as a stray line against the black footer. The dark band is
     distinct enough on its own at both edges. */
  border:0}
.mb-cta-dark h2{color:#fff}                     /* 17.40:1 */
.mb-cta-dark .mb-eyebrow{color:#CCCCCC}         /* 10.84:1 */
.mb-cta-dark .mb-lead,
.mb-cta-dark p{color:#E6E6E6}                   /* 13.94:1 */

/* KW Red on #1A1A1A measures 2.44:1 and FAILS AA as text, so the
   accent word is white here rather than red. Red stays on the button,
   where white-on-red is 7.13:1. Do not "restore" the red accent. */
.mb-cta-dark .mb-accent{color:#fff}

.mb-cta-dark .mb-cta-call a{color:#fff;text-decoration:underline;
  text-underline-offset:3px}
.mb-cta-dark .mb-icon{opacity:.75}
.mb-cta-dark a:focus-visible{outline:3px solid #fff;outline-offset:2px}

/* Checklist inverts to a translucent panel so it sits on the band
   without introducing another opaque rectangle. */
.mb-cta-dark .mb-check-list{background:rgba(255,255,255,.06);
  border-color:rgba(255,255,255,.16)}
.mb-cta-dark .mb-check-list li{color:#fff}
/* Red would be 2.44:1 on this panel; white keeps the ticks legible. */
.mb-cta-dark .mb-check-list .mb-icon{color:#fff;opacity:1}

/* CTA with a background photograph.
   MEASURED: white text on the raw image is 3.82:1 — it FAILS AA. The 70%
   overlay takes it to 11.63:1. Do not reduce the overlay below 0.65
   without re-measuring; the image is bright and the text is white. */
.mb-cta-image{position:relative;isolation:isolate;overflow:hidden}
.mb-cta-bg{position:absolute;inset:0;z-index:-2;display:block}
.mb-cta-bg img{width:100%;height:100%;object-fit:cover;display:block}
.mb-cta-image::before{content:"";position:absolute;inset:0;z-index:-1;
  background:rgba(26,26,26,.72)}
/* The band keeps its own background as a fallback: if the image fails to
   load, the section is still dark and the white text still legible. */
.mb-cta-image{background:#1A1A1A}

/* Hero trust markers — icon + label, separated by hairline rules.
   Dividers are ::before on siblings rather than border-right, so the
   last item never carries a trailing rule and a wrap never leaves one
   dangling at the end of a line. */
.mb-trust{list-style:none;margin:2rem 0 0;padding:0;
  display:flex;flex-wrap:wrap;align-items:center;gap:.75rem 1.5rem;
  /* Full ink at 600 weight: these are trust signals sitting directly
     under the primary CTA, so they should read as claims rather than as
     small print. ink (18.73:1) not ink-2 (5.74:1). */
  font-size:.9375rem;font-weight:600;color:var(--mb-ink)}
.mb-trust li{display:inline-flex;align-items:center;gap:.5em;margin:0;
  position:relative}
.mb-trust li + li::before{content:"";position:absolute;left:-.75rem;
  top:50%;transform:translateY(-50%);width:1px;height:1.1em;
  background:var(--mb-hairline)}
/* Icons in KW Red at full opacity. On white this is 7.13:1, so unlike
   the header there is no contrast objection to red here. */
.mb-trust .mb-icon{width:1.25em;height:1.25em;color:var(--mb-red);
  opacity:1;flex:0 0 auto;stroke-width:2}

/* Below 480px three items plus dividers will not fit one line. Stacking
   in two columns keeps them readable rather than letting them wrap into
   a ragged row with orphaned dividers. */
@media(max-width:480px){
  .mb-trust{gap:.625rem 1.125rem;font-size:.875rem}
  .mb-trust li + li::before{left:-.5625rem}
}

/* Twenty Twenty-Five applies a root-level block gap between the top-level
   template parts, which leaves a ~24px band of body background between
   <main> and <footer>. On a light page that reads as a white stripe above
   the black footer. The sections manage their own vertical rhythm, so the
   gap at this level is removed outright rather than compensated for. */
body > .wp-site-blocks > * + *{margin-block-start:0}
.wp-site-blocks{gap:0}

/* Eyebrow directly above a section heading: the heading's own top margin
   would otherwise open a gap and break the pairing. The two should read
   as one unit. */
.mb-eyebrow + h2{margin-top:0}

/* FOCUS — fixing an inherited parent-theme rule.
   Twenty Twenty-Five sets :where(.wp-site-blocks :focus){outline-width:2px;
   outline-style:solid} on plain :focus, NOT :focus-visible. That makes an
   outline appear on mouse click, which is not what a focus ring is for,
   and it inherits outline-color from our rule so it rendered KW Red.

   Plain :focus is neutralised and :focus-visible does the work, so the
   ring appears for keyboard users and not for mouse users.
   The ring itself is NOT removed — this is a correction, not a
   suppression. Keyboard navigation must stay visible. */
.wp-site-blocks :focus:not(:focus-visible){outline:none}

/* The header is black, where KW Red measures 2.44:1 against #000 and is
   effectively invisible as an indicator. White is 21:1. Applies to the
   logo link, nav and burger. */
.mb-header a:focus-visible,
.mb-header button:focus-visible{outline:3px solid #fff;outline-offset:3px;
  border-radius:2px}

/* The logo is content-box with clear-space padding, so a tight offset
   would cut into the KW clear space. Offset outward instead. */
.mb-header a:has(.mb-logo):focus-visible{outline-offset:2px}


/* Section head: heading block with an optional action link to its right.
   The link drops below the text on narrow screens rather than squeezing. */
.mb-section-head{display:flex;flex-wrap:wrap;gap:1rem 2rem;
  align-items:flex-start;justify-content:space-between;margin-bottom:3rem}
.mb-section-head > div{flex:1 1 30rem;min-width:0}
.mb-section-head .mb-section-intro-lg{margin-bottom:0}
.mb-section-link{margin:0;flex:0 0 auto}
.mb-section-link a{color:var(--mb-red);text-decoration:none;font-weight:500;
  font-size:.9375rem;display:inline-flex;align-items:center;min-height:44px}
.mb-section-link a:hover{text-decoration:underline;text-underline-offset:4px}
@media(min-width:900px){
  /* Optically align the link with the heading's baseline, not the
     eyebrow above it. Measured against the h2, not guessed. */
  .mb-section-link{padding-top:2.75rem}
}

/* Vertical rules between steps, as in the comp. Applied only from the
   3-column breakpoint: stacked on mobile they would sit above each step
   instead of between columns. */
@media(min-width:768px){
  .mb-steps{gap:0}
  .mb-step{padding-inline:2rem}
  .mb-step:first-child{padding-left:0}
  .mb-step:last-child{padding-right:0}
  .mb-step + .mb-step{border-left:1px solid var(--mb-hairline)}
}


/* Sections inside <main> must butt up against each other. Twenty
   Twenty-Five's block gap applies between sibling blocks here as well as
   at root level, and it shows as a band of body background between two
   coloured sections. The earlier .wp-site-blocks rule only covered the
   top-level children, not the patterns inside <main>.
   Each section owns its own padding, so no gap is wanted at this level. */
/* WordPress emits `:root :where(.is-layout-flow) > *{margin-block-start:1.5rem}`,
   whose :root prefix outweighs a plain `main > * + *`. Matching that
   specificity is the only way to win without !important. Verified against
   the computed value, not assumed. */
:root main.is-layout-flow > *,
:root main.wp-block-group > *{margin-block-start:0;margin-block-end:0}
main.wp-block-group{gap:0}

/* Scrollbar in KW Red.
   Two syntaxes on purpose: the standard `scrollbar-color` (Firefox and
   modern Chromium) and the WebKit pseudo-elements for older Safari and
   Chromium. Both are set so the thumb is red everywhere rather than red
   in one engine and default in another.
   Track stays light so the thumb reads as the moving element. */
html{scrollbar-color:var(--mb-red) var(--mb-surface-2);scrollbar-width:auto}
::-webkit-scrollbar{width:12px;height:12px}
::-webkit-scrollbar-track{background:var(--mb-surface-2)}
::-webkit-scrollbar-thumb{background:var(--mb-red);border-radius:6px;
  /* Inset border keeps the thumb visually narrower than the track
     without shrinking the grab target. */
  border:2px solid var(--mb-surface-2);background-clip:padding-box}
::-webkit-scrollbar-thumb:hover{background:var(--mb-red-hover);
  border-color:var(--mb-surface-2);background-clip:padding-box}
::-webkit-scrollbar-corner{background:var(--mb-surface-2)}


/* Header vertical balance on desktop.
   The logo is the tallest item in the nav row, so its own 20px padding
   sets the space above the artwork. Without a matching foot the band had
   20px at the top and 10px at the bottom, which read as bottom-heavy in
   reverse: the logo floated high.

   MEASURED on the rendered header, not estimated:
     space above logo artwork   20px  (logo padding-top)
     space below contact text   20px  (this rule)
   The contact strip is pulled up so it sits close to the nav row rather
   than drifting into the middle of the band. */
@media(min-width:1024px){
  .mb-header-inner{min-height:0}
  /* The logo no longer has bottom padding to cancel, so the pull is
     removed. The artwork's own baked-in clear space (19.23px at this
     size) separates it from the contact row. */
  .mb-header-contact{padding-bottom:20px;margin-top:0}
}

/* Contact strip pulled up toward the nav row, overriding both the
   -.5rem pull at line 126 and the desktop 0 above.

   TWO VALUES, and the difference is functional, not cosmetic.
   At -20px on a 390px screen the logo's box covers the centre of the
   phone link: measured logoBottom 83 against phoneTop 63, and a tap
   there hit IMG.mb-logo, so the number was neither readable nor
   dialable and went to the homepage instead. Desktop has the logo and
   the strip side by side, so it has room the narrow layout does not.
   -10px is the tightest value that keeps the phone number the hit
   target at 390px (verified: tap resolves to the tel: link). */
.mb-header-contact{margin-top:-10px!important}
@media(min-width:1024px){
  .mb-header-contact{margin-top:-20px!important} }

/* ---------- Hero portrait crossfade ---------- */
/* Two registered frames of the same portrait. The alt frame is stacked
   over the base and fades in on hover or keyboard focus.

   Why a crossfade and not a slide or a wipe: the two photographs share a
   pose, so the eyes, collar and tie land within a few pixels of each
   other. Fading between them reads as the light changing on a person who
   has not moved. Any transform would break that registration and
   reintroduce exactly the jumpiness we are avoiding.

   The stack establishes the box. The base frame is in normal flow and
   sets the height; the alt is taken out of flow on top of it. */
.mb-portrait-stack{position:relative;display:block;width:100%}
.mb-portrait-stack .mb-portrait-alt{position:absolute;inset:0}
.mb-portrait-stack .mb-portrait-alt img{height:100%;object-fit:cover}

/* Opacity only, so the browser composites on the GPU and never reflows.
   1.2s is deliberately slow: at the 300-400ms of a normal UI transition
   the change of background registers as a flicker. Slower reads as a
   considered shift. The ease is a gentle sine curve rather than the
   sharp cubic-bezier(.16,1,.3,1) used elsewhere, because an overshooting
   curve on a crossfade produces a visible bloom at the midpoint. */
.mb-portrait-alt{opacity:0;
  transition:opacity 1.2s cubic-bezier(.37,0,.63,1);
  will-change:opacity}
.mb-portrait:hover .mb-portrait-alt,
.mb-portrait:focus-within .mb-portrait-alt{opacity:1}

/* The fade out is slower still. Coming back to the base frame is the
   resting state, so letting it settle rather than snap keeps the whole
   interaction calm. */
.mb-portrait:not(:hover):not(:focus-within) .mb-portrait-alt{
  transition-duration:1.5s}

/* The reveal animation at line 630 transitions `.mb-portrait img`
   opacity. Both frames are inside that selector, so on first reveal the
   alt would fade up alongside the base and sit there at full opacity.
   Excluding it keeps the reveal on the base frame alone. */
.mb-motion .mb-portrait-alt img{transition:none}

/* Touch devices have no hover. Rather than leaving the second frame
   unreachable, tapping the figure toggles it via :focus-within, which
   needs the figure to be focusable. A slow auto-alternation was
   considered and rejected: unprompted movement beside the primary CTA
   competes with it for attention. */
@media(hover:none){
  .mb-portrait-alt{transition-duration:.9s}
}

/* Reduced motion: no crossfade at all. The base frame is the portrait
   with the accessible alt text, so pinning to it loses nothing. */
@media(prefers-reduced-motion:reduce){
  .mb-portrait-alt{transition:none!important}
  .mb-portrait:hover .mb-portrait-alt,
  .mb-portrait:focus-within .mb-portrait-alt{opacity:0}
}

/* ---------- Mobile logo alignment ---------- */
/* Below the desktop breakpoint the logo's left edge now lines up with the
   hero content: the eyebrow, the headline and the buttons all start at the
   container gutter, and the logo was starting 18px further in because it
   carried its own side padding on top of that gutter.

   This is brand-COMPLIANT, not a shortcut. KW clear space is a minimum
   distance to neighbouring elements and container edges; it does not have
   to be supplied by padding on the logo itself. The container gutter
   already exceeds it at every mobile size:

     200px logo  needs 12.92px  ->  20px gutter  (+7.08px spare)
     195px logo  needs 12.59px  ->  20px gutter  (+7.41px spare)

   Right padding is kept, since that edge faces the burger rather than the
   container edge and needs its own separation. Width budget re-checked at
   320px: 20+195+16+8+42+20 = 301px against 320px available.

   Desktop is deliberately untouched: the nav row is centred as a unit
   there and the logo is not aligning to a column of text below it. */
@media(max-width:1023px){
  /* padding-left:0 removes the CSS indent (18px).
     margin-left:-7.5px cancels the artwork's own blank canvas, MEASURED
     off the PNG in the browser: the first inked column is pixel 36 of
     960, so 3.75% of the rendered width is empty before the "k" begins.
     At the 200px mobile size that is 7.5px.

     A fixed -7.5px is used rather than a percentage: CSS percentage
     margins resolve against the PARENT's width, not the element's, so
     -3.75% drifts as the header width changes. Measured at 320px, where
     the logo drops to 195px, the fixed value lands within 0.19px while
     the percentage is 0.59px out.

     Verified in the browser at 390px and 320px: logo ink starts at 20px,
     the same x as the eyebrow, the h1 and the buttons. No burger overlap
     (logo right edge 223.49px against burger left 258.01px at 320px) and
     no document overflow. */
  .mb-logo{padding-left:0;margin-left:-7.5px}
}

/* ---------- Mobile menu motion ---------- */
/* The panel is clipped from its own top edge. GSAP animates clipPath
   when it is available; these values are the resting state and the
   no-GSAP fallback, so the menu still moves if the vendor file fails. */
.mb-mobile-nav{will-change:clip-path,opacity;transform:translateZ(0)}

/* Fallback transition for the no-GSAP path. GSAP sets inline styles that
   win over this, so the two never fight.
   -webkit- prefix first: Safari below 14 only supports the prefixed
   clip-path, and without it this fallback silently does nothing there. */
.mb-mobile-nav:not([data-mb-enhanced]){
  -webkit-transition:-webkit-clip-path .45s cubic-bezier(.16,1,.3,1);
  transition:clip-path .45s cubic-bezier(.16,1,.3,1)}

/* Burger glyph swaps between the bars and the cross. Rotating it during
   the swap would collide with the innerHTML change, so only colour and
   border move. */
.mb-burger{transition:border-color .25s ease,background-color .25s ease}
.mb-burger[aria-expanded="true"]{border-color:var(--mb-red);
  background:rgba(180,1,1,.12)}

/* ---------- iOS Safari ---------- */
/* Safari composites transform and opacity on the GPU but will fall back
   to the CPU for an element it has not promoted, which shows up as a
   stutter on the first frame of a reveal. Promoting the reveal targets
   up front avoids that. It is applied only while motion is active, so
   nothing is promoted needlessly once the reveals have finished. */
.mb-motion [data-mb-reveal]:not(.is-in){
  -webkit-backface-visibility:hidden;backface-visibility:hidden;
  -webkit-transform:translate3d(0,20px,0)}
.mb-motion [data-mb-reveal].is-in{-webkit-transform:translate3d(0,0,0)}

/* Safari needs the prefixed property on the portrait crossfade too,
   and an explicit 3D transform to keep the two frames on the same
   compositing layer. Without it the alt frame can flicker as it
   crosses the opacity threshold. */
.mb-portrait-alt{-webkit-backface-visibility:hidden;backface-visibility:hidden;
  -webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}

/* Tap highlight removed on interactive elements: iOS paints a grey box
   over the element during the tap, which lands on top of any animation
   running underneath it. Focus styles are untouched, so keyboard users
   keep their visible indicator. */
.mb-burger,.mb-mobile-nav a,.mb-btn,.mb-nav a,.mb-header-contact a{
  -webkit-tap-highlight-color:transparent}

/* iOS applies its own inner shadow and rounding to buttons and inputs
   unless this is set, which makes the burger look unlike the mockup. */
.mb-burger,.mb-btn{-webkit-appearance:none;appearance:none}

/* ---------- Steps: centred on mobile ---------- */
/* Below the 768px grid breakpoint the three steps stack into a single
   column. Left-aligned in a full-width column they read as a loose list;
   centred, each one reads as its own card.

   text-align on the step centres the counter/icon row too, because both
   are inline-level and follow the text alignment. The paragraph gets a
   max-width so a centred line does not run the full 351px, which is
   uncomfortable to read when ragged on both sides.

   Desktop is untouched: from 768px up the three sit side by side and
   left alignment is correct there. */
@media(max-width:767px){
  .mb-step{text-align:center}
  .mb-step p{max-width:34ch;margin-inline:auto}
  /* The icon carries a left margin to separate it from the counter on
     the shared row. Centred, that margin pushes the pair off-centre by
     half its width, so it is split to both sides. */
  .mb-step-icon{margin-left:.4375rem;margin-right:.4375rem}
}

/* ---------- FAQs ---------- */
/* <details>/<summary>: accordion behaviour with no JavaScript, keyboard
   accessible and screen-reader announced for free. */
.mb-faqs{max-width:820px;margin:3rem auto 0;
  border-top:1px solid var(--mb-hairline)}
.mb-faq{border-bottom:1px solid var(--mb-hairline)}

/* The default disclosure triangle is removed in favour of the chevron,
   which needs both the standard and the -webkit- form for Safari. */
.mb-faq summary{list-style:none;cursor:pointer;
  display:flex;align-items:center;justify-content:space-between;gap:1.5rem;
  padding:1.25rem 0;font-size:1.0625rem;font-weight:600;
  color:var(--mb-ink);line-height:1.4;
  /* min-height keeps the tap target comfortable on a phone */
  min-height:44px}
.mb-faq summary::-webkit-details-marker{display:none}
.mb-faq summary::marker{content:""}
.mb-faq summary:hover{color:var(--mb-red)}
.mb-faq summary:focus-visible{outline:3px solid var(--mb-red);
  outline-offset:3px;border-radius:2px}

.mb-faq-chev{width:1.25rem;height:1.25rem;flex:0 0 auto;
  color:var(--mb-ink-2);transition:transform .3s cubic-bezier(.16,1,.3,1),
  color .2s ease}
.mb-faq[open] .mb-faq-chev{transform:rotate(180deg);color:var(--mb-red)}
.mb-faq summary:hover .mb-faq-chev{color:var(--mb-red)}

.mb-faq-a{padding:0 0 1.5rem;color:var(--mb-ink-2);max-width:68ch}
.mb-faq-a p{margin:0 0 .75rem}
.mb-faq-a p:last-child{margin-bottom:0}
/* Bulleted answers (the "Why choose Morris Breuer?" reasons). Padding
   rather than list-style-position so wrapped lines align under the text,
   not under the marker. */
.mb-faq-a ul.mb-faq-list{margin:.75rem 0 0;padding:0 0 0 1.25rem}
.mb-faq-a ul.mb-faq-list li{margin:0 0 .75rem}
.mb-faq-a ul.mb-faq-list li:last-child{margin-bottom:0}
.mb-faq-a ul.mb-faq-list strong{color:var(--mb-ink)}
.mb-faq-a a{color:var(--mb-red);text-decoration:underline;
  text-underline-offset:3px}

/* The answer slides open rather than appearing. details does not
   animate its own height, so the inner div is animated instead.
   Guarded by [open] so a closed panel is not running a transition. */
.mb-faq[open] .mb-faq-a{animation:mb-faq-in .35s cubic-bezier(.16,1,.3,1)}
@keyframes mb-faq-in{
  from{opacity:0;transform:translateY(-6px)}
  to{opacity:1;transform:none} }
@media(prefers-reduced-motion:reduce){
  .mb-faq[open] .mb-faq-a{animation:none}
  .mb-faq-chev{transition:none} }

@media(max-width:767px){
  .mb-faqs{margin-top:2rem}
  .mb-faq summary{font-size:1rem;gap:1rem;padding:1.125rem 0}
}

/* FAQs page. The home page no longer carries FAQs; they live at /faqs
   in full. This closing block is the page's only call to action, since
   someone who has read this far has a question the page did not answer
   and wants a person, not another link. */
.mb-faqs-foot{max-width:820px;margin:3rem auto 0;text-align:center}
.mb-faqs-foot p{color:var(--mb-ink-2);margin:0 0 1.5rem}
.mb-faqs-foot-actions{display:flex;flex-wrap:wrap;gap:.75rem;
  justify-content:center;margin:0}
@media(max-width:479px){
  /* Stacked, full width: two side by side at this width leaves each
     too narrow for the phone number to sit on one line. */
  .mb-faqs-foot-actions{flex-direction:column}
  .mb-faqs-foot-actions .mb-btn{width:100%} }

/* ---------- Articles index and single post ---------- */

/* The intro section already carries the section padding, so the grid
   sits directly beneath it with only its own bottom spacing. */
.mb-articles-head{padding-bottom:0}
.mb-articles-wrap{padding-bottom:6rem}

.mb-article-grid{display:grid;gap:3rem 2rem;list-style:none;
  padding:0;margin:0}
@media(min-width:640px){.mb-article-grid{grid-template-columns:1fr 1fr}}
@media(min-width:1024px){.mb-article-grid{grid-template-columns:repeat(3,1fr)}}

.mb-article-grid > li{display:flex;flex-direction:column;min-width:0}

.mb-article-thumb{margin:0 0 1.25rem}
.mb-article-thumb img{width:100%;height:auto;display:block;
  border-radius:4px;background:var(--mb-surface)}
/* A slow, small lift. A card that jumps under the cursor reads as a
   button, and these are articles, not actions. */
.mb-article-thumb a{display:block;overflow:hidden;border-radius:4px}
.mb-article-thumb img{transition:transform .6s cubic-bezier(.16,1,.3,1)}
.mb-article-grid > li:hover .mb-article-thumb img{transform:scale(1.03)}

.mb-article-date{font-size:.8125rem;color:var(--mb-ink-2);
  margin:0 0 .5rem;letter-spacing:.02em}
.mb-article-title{font-size:1.125rem;line-height:1.35;margin:0 0 .75rem}
.mb-article-title a{color:var(--mb-ink);text-decoration:none}
.mb-article-title a:hover{color:var(--mb-red)}
.mb-article-excerpt{font-size:.9375rem;line-height:1.6;
  color:var(--mb-ink-2);margin:0}
.mb-article-excerpt a{color:var(--mb-red)}

.mb-article-pagination{display:flex;gap:1rem;flex-wrap:wrap;
  margin-top:4rem;padding-top:2rem;
  border-top:1px solid var(--mb-hairline)}
.mb-article-pagination a{color:var(--mb-red);text-decoration:none}
.mb-article-pagination a:hover{text-decoration:underline;
  text-underline-offset:4px}

@media(prefers-reduced-motion:reduce){
  .mb-article-thumb img{transition:none}
  .mb-article-grid > li:hover .mb-article-thumb img{transform:none} }

/* Key takeaways and sources, appended to each imported article. They
   are real blocks rather than a template flourish, so Morris can edit
   or delete them per post. */
.mb-takeaways{background:var(--mb-surface);border-radius:4px;
  padding:2rem;margin-block:3rem}
.mb-takeaways h2{font-size:1.0625rem;margin:0 0 1rem;
  letter-spacing:.06em;text-transform:uppercase}
.mb-takeaways ul{margin:0;padding-left:1.25rem}
.mb-takeaways li{margin-bottom:.625rem;line-height:1.6}
.mb-takeaways li:last-child{margin-bottom:0}

.mb-sources{margin-block:3rem;padding-top:2rem;
  border-top:1px solid var(--mb-hairline)}
.mb-sources h2{font-size:.875rem;margin:0 0 .875rem;
  letter-spacing:.06em;text-transform:uppercase;color:var(--mb-ink-2)}
.mb-sources ul{margin:0;padding-left:1.25rem;font-size:.9375rem}
.mb-sources li{margin-bottom:.375rem}
.mb-sources a{color:var(--mb-red)}

/* Single article. 720px measure: long market copy at the 1200px wide
   size would run to ~120 characters a line, well past comfortable. */
.mb-article-single{max-width:760px;margin-inline:auto;
  padding:4rem 20px 5rem}
@media(min-width:768px){.mb-article-single{padding:5rem 32px 6rem}}
.mb-article-single h1{font-size:clamp(1.75rem,4vw,2.5rem);
  line-height:1.15;margin:.5rem 0 1.5rem}
.mb-article-hero{margin:0 0 2.5rem}
.mb-article-hero img{width:100%;height:auto;border-radius:4px}
.mb-article-single p{line-height:1.7;margin-bottom:1.25rem}
.mb-article-single h2{font-size:1.375rem;line-height:1.3;
  margin:2.5rem 0 1rem}
.mb-article-single ul{line-height:1.7;padding-left:1.25rem}
.mb-article-single li{margin-bottom:.5rem}

/* ============================================================
   ABOUT PAGE
   ============================================================
   Added 22 Sep 2026. These rules did not previously exist, which
   is why /about/ rendered unstyled: .mb-bio was an unstyled div,
   so its two children stacked, the portrait dropped below the
   text and centred, and the lead ran the full 1200px measure.
   A previous session's log recorded this block as deployed; it
   was not. Verify against the served file, not the log. */

/* Mirrors .mb-hero-grid rather than redefining the proportion,
   so About and the homepage share one column ratio. Kept as its
   own class because the bio column carries a list the hero does
   not, and because .mb-hero-grid's first-child top padding is
   tuned to the hero's H1 size, not this one. */
.mb-bio{display:grid;gap:2.5rem;align-items:start}
@media(min-width:900px){
  .mb-bio{grid-template-columns:1fr .8fr;gap:4rem}
}

/* The About H1 is one line shorter than the hero's, so it takes a
   slightly tighter clamp — matching the hero exactly left a gap
   above the lead that read as a mistake at 1440px. */
.mb-bio h1{font-size:clamp(2.25rem,1.8rem + 2.6vw,3.5rem);
  line-height:1.1;letter-spacing:-.02em;margin:0 0 1.5rem}

/* 60ch, not the section default 66ch. The lead sits in a 1fr
   column beside the portrait, so it is already narrower than a
   full-width intro; 66ch would not constrain it at all below
   1200px and the limit would never apply. */
.mb-bio .mb-lead{max-width:60ch;margin:0 0 2rem}

/* Icon list spacing on About only. The footer's copy of this
   component is 14px type in a dark column; here it is body size
   on white and needs more room between rows to stop the three
   facts reading as one paragraph. */
.mb-bio .mb-icon-list{gap:.875rem;margin-top:2rem}
.mb-bio .mb-icon-list > li{align-items:center}
.mb-bio .mb-icon-list .mb-icon{margin-top:0;opacity:1;
  color:var(--mb-red)}

/* Portrait on About.
   justify-self:end so the photo sits against the text column
   rather than floating centred in its own track — .mb-portrait
   defaults to centre for the homepage's narrower hero column.
   The warm offset panel and badge are inherited unchanged, so
   the treatment stays identical across the two pages. */
.mb-bio-portrait{justify-self:center;width:min(100%,380px)}
@media(min-width:900px){
  .mb-bio-portrait{justify-self:end;margin-top:.5rem}
}

/* .mb-portrait-base exists because the homepage stacks two frames
   for a crossfade and needs a positioned base. About renders a
   single frame, so this only has to not break the inherited
   position:relative on .mb-portrait picture. */
.mb-portrait-base{position:relative;display:block;width:100%}

/* ============================================================
   GAP MARKERS — build annotations, not content
   ============================================================
   Added 22 Sep 2026 alongside the inner-page CSS below; these
   rules were missing, so every gap block rendered as plain body
   copy indistinguishable from real page text. That is the
   dangerous failure mode: an unconfirmed fact reading as a
   published claim.

   Deliberately styled as an annotation — dashed rule, amber
   flag, uppercase label — so nobody can mistake one for copy.
   Amber (not red) because red is reserved for the valuation
   path; a red panel here would compete with the CTA and imply
   an error rather than an open question.

   These blocks are expected to DISAPPEAR before launch, as each
   fact is confirmed. They are not a permanent component. */
.mb-gap{border:1px dashed #C8A24A;border-left:4px solid #C8A24A;
  background:#FDFAF3;border-radius:4px;padding:1.25rem 1.5rem;
  margin-block:1.5rem}
.mb-gap p{margin:0;color:var(--mb-ink-2);font-size:.9375rem;
  line-height:1.6;max-width:68ch}
.mb-gap-label{font-size:.75rem!important;font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;
  color:#8A6A1F!important;margin-bottom:.5rem!important}

/* Inside a property card the marker shares the card's padding,
   so it loses its own horizontal inset and sits flush. */
.mb-gap-inline{margin-block:1rem 0;padding:1rem 1.125rem}
.mb-gap-inline p{font-size:.875rem}

/* A gap that replaces a single paragraph inside a step, rather
   than a whole block. Same amber vocabulary, minimal chrome —
   a full panel inside a step would outweigh the step itself. */
.mb-gap-inline-text{border-left:3px solid #C8A24A;
  padding-left:.875rem;color:var(--mb-ink-2);font-style:italic}

/* ============================================================
   CONTACT PAGE
   ============================================================ */

/* Three equal cards. Same breakpoints as .mb-cards so the two
   card grids on the site change shape at the same widths. */
.mb-contact-grid{display:grid;gap:1.5rem;margin-top:3rem}
@media(min-width:700px){.mb-contact-grid{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1024px){.mb-contact-grid{grid-template-columns:repeat(3,1fr)}}

/* Borrows .mb-card's surface treatment but not its hover lift —
   these are not links, and a lift would promise a click that is
   not there. The icon reuses .mb-step-icon unchanged. */
/* Navy cards, white text. These three are reference, not action: the
   page's one action is the form below, and a solid navy block reads as
   information while leaving red unspent for the button. Navy is an
   observed KW web colour rather than a guide token, which is exactly
   why it carries reference and never a call to action.
   Nothing red goes on this surface: --mb-red on --mb-navy is 1.97:1
   and effectively illegible. */
.mb-contact-card{background:var(--mb-navy);border:1px solid var(--mb-navy);
  border-radius:8px;padding:2rem;color:#fff}
.mb-contact-card h2{font-size:1.125rem;margin:1.25rem 0 .75rem;
  letter-spacing:-.01em;color:#fff}

/* Translucent white disc: the glyph has to read against navy, so the
   red circle used by the steps on home, about and sell is wrong here.
   Scoped to .mb-contact-card so those keep it unchanged. */
.mb-contact-card .mb-step-icon{margin-left:0;
  background:rgba(255,255,255,.12);color:#fff}

/* The value is the point of the card, so it carries the weight.
   On the navy card it is plain white; the ink/red rules below are
   for .mb-contact-value used outside a contact card. */
.mb-contact-value{font-size:1.125rem;font-weight:700;margin:0;
  letter-spacing:-.01em;word-break:break-word}
.mb-contact-card .mb-contact-value a{color:#fff;text-decoration:none}
/* Hover underlines rather than recolouring: red on navy is illegible
   and there is no lighter accent in the palette that would not read
   as a second brand colour. */
.mb-contact-card .mb-contact-value a:hover{color:#fff;
  text-decoration:underline;text-underline-offset:3px}
/* rgba white rather than --mb-ink-2, which is a grey mixed for white
   backgrounds and muddies against navy. .78 gives 9.12:1, still AAA. */
.mb-contact-card .mb-contact-note{color:rgba(255,255,255,.78)}
.mb-contact-note{font-size:.875rem;color:var(--mb-ink-2);
  margin:.5rem 0 0}

/* Hours table beside form. Not 1fr 1fr: the form needs more
   room than a seven-row table, and an equal split left the
   table stranded in white space. */
.mb-two-col{display:grid;gap:3rem}
@media(min-width:900px){
  .mb-two-col{grid-template-columns:.85fr 1fr;gap:4rem;align-items:start}
}

/* Hours: a real table, so it reads correctly to a screen reader
   and the closed day is a value rather than an empty cell. */
.mb-hours{width:100%;border-collapse:collapse;margin-top:1.5rem;
  max-width:26rem}
.mb-hours th,.mb-hours td{text-align:left;padding:.75rem 0;
  border-bottom:1px solid var(--mb-hairline);font-weight:400;
  font-size:.9375rem}
.mb-hours th{color:var(--mb-ink);width:45%}
.mb-hours td{color:var(--mb-ink-2);text-align:right}
.mb-hours tr:last-child th,.mb-hours tr:last-child td{border-bottom:0}
/* Closed day de-emphasised rather than hidden — absence of a
   value is information. */
.mb-hours .is-closed th,.mb-hours .is-closed td{color:#8C8C8C}

.mb-form-head{margin-top:0}

/* FluentForm reset. The plugin ships its own button and input
   treatment; these rules pull it onto the site's vocabulary
   without touching plugin files, so an update cannot revert it. */
.mb-form{margin-top:1.5rem}
.mb-form .ff-el-input--label label{font-weight:500;font-size:.9375rem;
  color:var(--mb-ink)}
.mb-form input[type="text"],.mb-form input[type="email"],
.mb-form input[type="tel"],.mb-form textarea,.mb-form select{
  width:100%;border:1px solid var(--mb-hairline);border-radius:6px;
  padding:.75rem .875rem;font-size:1rem;font-family:inherit;
  color:var(--mb-ink);background:#fff}
.mb-form textarea{min-height:9rem;resize:vertical}
.mb-form input:focus-visible,.mb-form textarea:focus-visible{
  outline:3px solid var(--mb-red);outline-offset:2px;
  border-color:var(--mb-red)}
/* Submit matches .mb-btn-primary exactly. Written out rather
   than composed because the plugin renders its own class on the
   button and we cannot add ours to the markup. */
.mb-form .ff-btn-submit,.mb-form button[type="submit"]{
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;padding:.625rem 1.5rem;border-radius:6px;
  background:var(--mb-red);color:#fff;border:1px solid transparent;
  font-size:1rem;font-weight:700;font-family:inherit;line-height:1.2;
  cursor:pointer;transition:background-color .15s ease}
.mb-form .ff-btn-submit:hover,.mb-form button[type="submit"]:hover{
  background:var(--mb-red-hover)}

/* ============================================================
   PROPERTIES PAGE
   ============================================================ */

/* Description sits between the facts row and the reference
   line. .mb-card-facts uses margin:auto 0 0 to push itself to
   the card foot, so anything after it must not fight for that
   space — these two stay natural height. */
.mb-card-desc{font-size:.9375rem;color:var(--mb-ink-2);
  line-height:1.6;margin:.75rem 0 0}

/* Reference line: the smallest, quietest text on the card.
   Carries price ranges and listing refs — useful, never the
   thing read first. */
.mb-card-ref{font-size:.8125rem;color:var(--mb-ink-2);
  margin:.75rem 0 0;padding-top:.75rem;
  border-top:1px solid var(--mb-hairline)}

/* Required disclosure (digitally enhanced imagery). Must stay
   legible — it is a compliance statement, not fine print — so
   it takes an icon and ink-2 rather than being shrunk away. */
.mb-card-note{display:grid;grid-template-columns:1em 1fr;
  gap:.5em;align-items:start;font-size:.8125rem;
  color:var(--mb-ink-2);margin:.75rem 0 0}
.mb-card-note .mb-icon{width:1em;height:1em;margin-top:.2em;opacity:.7}

/* Sold card reads as past tense: the photo desaturates, the
   body does not. Desaturating text would hurt contrast. */
.mb-card.is-sold .mb-card-media img{filter:grayscale(1);opacity:.85}

/* ============================================================
   FAQS PAGE
   ============================================================ */

/* The FAQ accordion already has full styling (see above). These
   rules cover only the page-level wrapper and its footer. */
.mb-faqs-section .mb-section-intro{margin-bottom:0;max-width:none}
/* The heading is an h1 here (standalone page) but was sized as a
   section h2. Match the other interior pages' h1 so /faqs/ does not
   arrive visually heavier than /about/. */
.mb-faqs-section h1{font-size:clamp(2.25rem,1.8rem + 2.6vw,3.5rem);
  line-height:1.1;letter-spacing:-.02em;margin:0}
.mb-lede{font-size:1.125rem;color:var(--mb-ink-2);max-width:60ch;
  margin:1rem 0 0}

/* Inline link with a trailing arrow, for use inside answer copy
   where a full button would break the paragraph rhythm. */
.mb-link-arrow{color:var(--mb-red);font-weight:700;
  text-decoration:none;display:inline-flex;align-items:center;gap:.4em}
.mb-link-arrow::after{content:"\2192";transition:transform .15s ease}
.mb-link-arrow:hover{text-decoration:underline;text-underline-offset:3px}
.mb-link-arrow:hover::after{transform:translateX(3px)}
@media(prefers-reduced-motion:reduce){
  .mb-link-arrow::after{transition:none}
  .mb-link-arrow:hover::after{transform:none}
}

/* Ghost button: the phone number beside the primary CTA. Lower
   weight than .mb-btn-secondary so the valuation button stays
   the single obvious action in the footer block. */
.mb-btn-ghost{border-color:var(--mb-hairline);color:var(--mb-ink);
  background:#fff}
.mb-btn-ghost:hover{border-color:var(--mb-ink);background:var(--mb-surface)}

.mb-faqs-foot{margin-top:3.5rem;padding-top:2.5rem;
  border-top:1px solid var(--mb-hairline);text-align:center}
.mb-faqs-foot p{margin:0 0 1.5rem;color:var(--mb-ink-2)}
.mb-faqs-foot-actions{display:flex;flex-wrap:wrap;gap:1rem;
  justify-content:center;margin:0!important}

/* ============================================================
   Split hero (Sell page)
   Reuses .mb-split. Only three things differ from the standard
   split section: the media sits on the RIGHT, the text column is
   a hero rather than a mid-page block, and the image fades into
   the white text panel instead of meeting it on a hard edge.

   A dark full-bleed image hero was tried here and reverted: it made
   /sell the only page not opening on white, and the dark backdrop
   forced .mb-accent to white, losing the red accent every other h1
   carries. Text now sits on #fff, so the normal body contrast
   applies and nothing here needs a measured override.
   ============================================================ */
.mb-split-hero{padding:0}

/* Source order is text-then-media so the h1 leads the document.
   On desktop the media is pulled to the right visually; on mobile
   the natural order already puts the heading first. */
@media(min-width:900px){
  .mb-split-hero{grid-template-columns:1fr 1fr}
  .mb-split-hero .mb-split-body{order:1}
  .mb-split-hero .mb-split-media{order:2}
}

/* The fade. A MASK on the image, not a white overlay on top of it.

   Two earlier attempts used a ::after gradient of white over the photo.
   Both failed the same way: the overlay paints white ONTO the subject,
   so a fade wide enough to soften the seam also puts a milky haze
   across the station frontage (measured: 30% of a 713px column is
   214px of white sitting on the building). Masking instead makes the
   image itself go transparent, so the white showing through is the
   page background and the subject is never veiled.

   -webkit- prefix included: Safari still needs it.
   Horizontal on desktop, fading left toward the text; vertical on
   mobile, fading up toward the heading above it. */
.mb-split-hero .mb-split-media img{
  -webkit-mask-image:linear-gradient(180deg,transparent 0%,#000 26%);
  mask-image:linear-gradient(180deg,transparent 0%,#000 26%)}
@media(min-width:900px){
  .mb-split-hero .mb-split-media img{
    -webkit-mask-image:linear-gradient(90deg,transparent 0%,#000 34%);
    mask-image:linear-gradient(90deg,transparent 0%,#000 34%)}
}
/* The media column's own background would show as a grey block through
   the masked area, so it matches the body panel instead. */
.mb-split-hero .mb-split-media{background:#fff}

/* Hero proportions: taller than a mid-page split so the opening has
   presence, but the image is 1.82:1 and cover crops the SIDES once the
   column is narrower than that, so height is kept modest on mobile to
   avoid cropping the station frontage out of frame. */
.mb-split-hero .mb-split-media{min-height:15rem}
@media(min-width:900px){
  .mb-split-hero .mb-split-media{min-height:32rem}
}

/* h1 in a split body, where the base rule only styles h2. */
.mb-split-hero h1{margin-top:.25rem}
.mb-split-hero .mb-hero-actions{margin-top:2rem}

/* ============================================================
   Map embed (Sell page)
   Full content width, fluid. The supplied embed was fixed at
   600x450; aspect-ratio keeps it proportional at every width
   instead, and the iframe fills the wrapper.
   ============================================================ */
.mb-map{position:relative;margin-top:2.5rem;
  /* Wide on desktop so it reads as a band rather than a box, but
     squarer on mobile where a 21:9 strip would show almost no
     useful area. */
  aspect-ratio:4/3;
  border:1px solid var(--mb-hairline);
  background:var(--mb-surface-2);
  overflow:hidden}
@media(min-width:768px){.mb-map{aspect-ratio:16/9}}
@media(min-width:1024px){.mb-map{aspect-ratio:21/9}}
.mb-map iframe{position:absolute;inset:0;width:100%;height:100%;
  border:0;display:block}

/* ============================================================
   VALUATION PAGE — form beside image
   ============================================================ */

/* A variant of .mb-split, not a new layout. Two things differ,
   both caused by the body column holding a FORM rather than a
   short block of copy.

   1. The form makes this column much taller than any other
      .mb-split body. The base rule centres the body vertically
      (align-items:center) and gives the media a fixed min-height,
      which together would leave a tall form floating in a short
      section. Here the body aligns to the top and the media
      stretches to whatever height the form needs.

   2. Padding is tighter at the top so the first field sits as
      high as the layout allows. On a conversion page the form
      being visible without scrolling is worth more than the
      breathing room. */
.mb-split-form .mb-split-body{align-items:flex-start;
  padding-top:2.5rem;padding-bottom:3rem}
@media(min-width:900px){
  .mb-split-form .mb-split-body{padding-top:3.25rem;padding-bottom:4rem}
}
/* Form copy can run slightly wider than hero copy without hurting
   legibility, because labels and fields break the line naturally. */
.mb-split-form .mb-split-inner{max-width:33rem}

/* The image column must not force the section taller than the form
   needs, so min-height drops to a value that still shows a usable
   crop on mobile, and the desktop floor is removed entirely — the
   grid's align-items:stretch already matches it to the form. */
/* MOBILE ORDER. The base .mb-split stacks body-then-media, which puts
   the photograph BELOW the submit button on a phone - the one place it
   cannot do its job. Source order stays body-first so the h1 leads the
   document and screen readers meet the heading before the image; only
   the visual order flips.

   Above 900px the columns sit side by side and the natural order is
   already correct (form left, image right, as requested), so this only
   applies below that. */
@media(max-width:899px){
  .mb-split-form{display:flex;flex-direction:column}
  .mb-split-form .mb-split-body{order:2}
  .mb-split-form .mb-split-media{order:1}
}

/* Mobile height. Measured at a 502px viewport the column came out
   487x224 - an aspect ratio of 2.17 against the image's 0.75, which
   letterboxed a portrait photograph into a strip showing roof tiles
   and little else. 17rem is tall enough to read as a house without
   pushing the form off the screen.

   Above 900px the floor is removed: the grid's align-items:stretch
   already matches the column to the form beside it. */
.mb-split-form .mb-split-media{min-height:17rem}
@media(min-width:900px){.mb-split-form .mb-split-media{min-height:0}}

/* Crop anchor.

   The live axis here is VERTICAL: measured box 800x1004 (AR 0.797)
   against the image's 896x1200 (AR 0.747). The box is WIDER than the
   image, so cover scales to width and crops top and bottom, which
   makes object-position's Y value the one that does the work. (The
   opposite case caught us on the /sell hero, where the box was
   narrower than the image and a Y value did nothing at all. Check
   which axis is live before tuning this.)

   60% rather than centre. The recognisable part of a house is the
   door, bay window and front garden, not the roof - at the default
   50% the roofline ate the top of the frame and the door sat low.
   Nudging the window down keeps the ground floor comfortably in
   view. */
.mb-split-form .mb-split-media-top img{object-position:50% 60%}

/* Reassurance line under the submit button. Smaller and quieter
   than body copy: it answers a hesitation, it is not a claim the
   page is making. */
.mb-form-note{margin-top:1.5rem;font-size:.9375rem;line-height:1.6;
  color:var(--mb-ink-2);max-width:32rem}

/* H1 matched to the other interior pages (.mb-bio, .mb-faqs-section)
   rather than the homepage hero scale. Measured at 1440px it was
   rendering at 67px, which on a page whose job is the form pushed the
   first field below the fold for no benefit. Capped at 3.5rem it
   still reads as the page heading and the form starts higher. */
.mb-split-form h1{font-size:clamp(2.25rem,1.8rem + 2.6vw,3.5rem);
  line-height:1.12;margin-top:.25rem}

/* Submit button.

   FluentForm renders the blue not from its own stylesheet but from an
   INLINE <style> block it prints with the form:

     form.fluent_form_3 .ff-btn-submit:not(.ff_btn_no_style){
       background-color:var(--fluentform-primary)}

   An inline block is appended after the theme stylesheet and that
   selector is more specific than a .mb-form one, so the earlier
   attempt here lost on both counts. Verified by enumerating the rules
   that actually matched the button, not by guessing.

   So set the CUSTOM PROPERTY instead of fighting the selector. It is
   the plugin's own override point, it keeps the cascade intact, and it
   needs no !important. Scoped to .mb-form so it cannot alter a form
   rendered anywhere else.

   Measured after the fix: white on #B40101 is 7.13:1, which clears
   WCAG AA (4.5:1) for the button label. The plugin blue it replaces
   measured 3.87:1 and FAILED AA. */
.mb-form{--fluentform-primary:var(--mb-red);
  --fluentform-primary-hover:var(--mb-red-hover)}
.mb-form .ff-btn-submit{border-radius:6px;font-weight:700;
  font-family:inherit;min-height:44px;padding:.625rem 1.5rem}
.mb-form .ff-btn-submit:focus-visible{
  outline:3px solid var(--mb-ink);outline-offset:2px}

/* Image column height.

   Two failed attempts are worth recording, because both looked
   plausible and neither worked:

   1. picture{position:sticky;height:100vh} pinned the photo to the
      viewport, but the column tracks the FORM, which is taller.
      Measured 1004px of column against a 718px picture: 286px of bare
      background showed below the photo.

   2. height:min(100vh,100%) did not fix it. On a grid item with no
      explicit height the child's 100% has no definite height to
      resolve against, so it collapsed back to the viewport value.
      Computed height stayed at 717.5px.

   The base .mb-split-media rule already does the right thing - it
   absolutely positions the IMG to inset:0 and lets the grid stretch
   the column. The only reason this variant broke was the extra
   <picture> wrapper sitting between the two, which is not in the other
   .mb-split sections. Making the wrapper display:contents removes it
   from layout entirely, so the base rule reaches the img as designed.

   No sticky. It was tried and dropped: pinning the photo needs a
   definite height that fights the stretch above, and a form this short
   does not scroll far enough for sticky to earn that complexity. */
.mb-split-form .mb-split-media{background:var(--mb-surface-2)}
.mb-split-form .mb-split-media picture{display:contents}

/* ============================================================
   MOTTO AND AFFILIATION
   ============================================================ */

/* "Because people come before property" - confirmed by the client
   23 Sep 2026, NOT present in the evidence archive.

   Set in KW Red italic at a size between the h1 and the lead. Red
   because it is the brand's accent and this is the brand's line;
   italic because it is a statement of approach rather than a claim,
   and the slant separates it from the propositional copy around it
   without needing a rule or a box.

   Measured: #B40101 on white is 7.13:1, clearing WCAG AA (4.5:1) and
   AAA (7:1) for body text. It is set at 1.125rem minimum, so the
   large-text exemption is not being leaned on. */
.mb-motto{font-size:clamp(1.125rem,1.05rem + .35vw,1.3rem);
  font-style:italic;color:var(--mb-red);line-height:1.4;
  margin:.75rem 0 1.25rem;max-width:32ch}

/* About page treatment. A blockquote rather than a paragraph, sitting
   under the one biographical sentence the archive confirms - the two
   say the same thing, so the motto reads as its short form.

   The red left rule does the quoting rather than quote marks, which
   would imply it is a citation from somewhere. */
.mb-motto-quote{margin:1.5rem 0 0;padding:0 0 0 1.25rem;
  border-left:3px solid var(--mb-red)}
.mb-motto-quote p{font-size:clamp(1.125rem,1.05rem + .35vw,1.3rem);
  font-style:italic;color:var(--mb-red);line-height:1.4;margin:0}

/* Footer motto. White rather than red: KW Red on black measures
   2.95:1 and would FAIL WCAG AA (4.5:1), and the guide forbids
   manufacturing a lighter red variant to get around that. Full-opacity
   white on #000 is 21:1; the footer's default .75 alpha is 11.42:1.
   Using full white here lifts the motto above the surrounding links
   without introducing a second colour. */
.mb-footer-motto{color:#fff;font-style:italic;font-size:1rem;
  line-height:1.4;margin:0 0 .5rem}

/* Coverage areas, immediately under the motto. Kept at the footer's
   default .75 alpha (11.42:1) so it supports the motto rather than
   competing with it. */
.mb-footer-areas{font-size:.875rem;line-height:1.5;margin:0}

/* Colophon affiliation line. Same .75 alpha as the copyright beside
   it, because the two are peers: one is the legal owner, the other
   the network relationship. */
.mb-colophon-affiliation{text-align:right}
@media(max-width:600px){.mb-colophon-affiliation{text-align:left}}

/* ================================================================
   LISTINGS
   The properties page and the listing detail view. Both read from
   the mb_listing post type; nothing here is hard-coded per property.
   ================================================================ */

/* Card action. Pushed to the bottom of the body with margin-top:auto
   so every button in a row sits on the same line regardless of how
   long the description above it ran. The card is already a flex
   column, which is what makes that work.

   Secondary, not primary: a page of three red buttons would spend the
   whole red budget on navigation and leave the valuation CTA below
   competing with them. Red stays for the one action that matters. */
.mb-card-actions{margin:1.25rem 0 0;padding-top:0}
.mb-card-body .mb-card-actions{margin-top:auto}
.mb-card-cta{width:100%}

/* The media is wrapped in a link as well as the button. It is
   aria-hidden and tabindex=-1 so the keyboard and screen readers get
   exactly one link per card, not two that say the same thing. */
.mb-card-media a{display:block;position:absolute;inset:0}

/* ---------- Listing detail ---------- */
.mb-listing{padding:2rem 0 4rem}
.mb-listing-back{margin:0 0 1.5rem;font-size:.875rem}
.mb-listing-back a{color:var(--mb-ink-2);text-decoration:none}
.mb-listing-back a:hover{color:var(--mb-ink);text-decoration:underline;
  text-underline-offset:3px}

/* Gallery. One large stage plus a scrolling thumbnail strip. The
   stage keeps a fixed aspect ratio so swapping images cannot shift
   the page under the reader. */
.mb-listing-gallery{margin:0 0 2.5rem}
.mb-gallery-main{position:relative;margin:0;aspect-ratio:3/2;
  background:var(--mb-surface-2);border-radius:8px;overflow:hidden}
.mb-gallery-main img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover}
.mb-gallery-count{position:absolute;right:1rem;bottom:1rem;margin:0;
  background:rgba(0,0,0,.72);color:#fff;font-size:.8125rem;
  padding:6px 10px;border-radius:4px}

.mb-gallery-thumbs{display:flex;gap:.5rem;list-style:none;margin:.75rem 0 0;
  padding:0 0 .5rem;overflow-x:auto;scrollbar-width:thin}
.mb-gallery-thumbs li{flex:0 0 auto;margin:0}
.mb-thumb{width:96px;aspect-ratio:3/2;padding:0;border:2px solid transparent;
  border-radius:4px;overflow:hidden;background:var(--mb-surface-2);
  cursor:pointer;display:block;
  transition:border-color .15s ease,opacity .15s ease}
.mb-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.mb-thumb:hover{opacity:.85}
.mb-thumb.is-active{border-color:var(--mb-red)}


.mb-listing-layout{display:grid;gap:2.5rem}
@media(min-width:1024px){
  .mb-listing-layout{grid-template-columns:minmax(0,1fr) 340px;
    align-items:start}
  /* Sticky within its column, offset for the fixed header. */
  .mb-listing-aside{position:sticky;top:6rem}
}

.mb-listing-addr{font-size:1.75rem;margin:.25rem 0 1rem;
  letter-spacing:-.01em}
.mb-listing-h2{font-size:1.25rem;margin:2.5rem 0 1rem;
  letter-spacing:-.01em}
.mb-listing-desc{color:var(--mb-ink);line-height:1.7}
.mb-listing-desc p{margin:0 0 1rem}

.mb-listing-highlights{list-style:none;margin:0;padding:0;display:grid;
  gap:.625rem}
@media(min-width:700px){
  .mb-listing-highlights{grid-template-columns:repeat(2,1fr)}
}
.mb-listing-highlights li{display:flex;gap:.625rem;align-items:flex-start;
  margin:0;font-size:.9375rem;line-height:1.5}
.mb-listing-highlights svg{flex:0 0 auto;width:18px;height:18px;
  margin-top:2px;color:var(--mb-red)}

.mb-floorplan{display:block;position:relative;border:1px solid var(--mb-hairline);
  border-radius:8px;overflow:hidden;background:#fff}
.mb-floorplan img{display:block;width:100%;height:auto}
.mb-floorplan-hint{position:absolute;right:1rem;bottom:1rem;
  background:rgba(0,0,0,.72);color:#fff;font-size:.8125rem;
  padding:6px 10px;border-radius:4px}

/* The enquiry block reuses the navy contact card. Same reasoning as
   /contact: navy carries reference, red carries the one action. */
.mb-listing-enquire .mb-listing-actions{margin:1.25rem 0 0}
.mb-listing-enquire .mb-btn{width:100%}

.mb-listing-spec{margin:1.5rem 0 0;padding:1.5rem 0 0;
  border-top:1px solid var(--mb-hairline);display:grid;gap:.75rem}
.mb-listing-spec div{display:flex;justify-content:space-between;gap:1rem;
  font-size:.9375rem}
.mb-listing-spec dt{color:var(--mb-ink-2)}
.mb-listing-spec dd{margin:0;font-weight:500;text-align:right}

/* ---------- Listing detail: motion and interaction ----------
   The gallery and listing.js layer on top of the static page above.
   Same rules as the rest of the motion system: only transform and
   opacity animate, nothing is hidden unless JS will show it, and
   reduced motion gets instant changes rather than none. */

/* First screen, composed by listing.js. Hidden only under .mb-motion
   and only until .is-intro lands. The animation is a failsafe: if the
   script never runs, everything appears at 2.5s regardless. The hero
   photo is deliberately NOT in this list, because it is the LCP. */
.mb-motion .mb-listing:not(.is-intro) .mb-price,
.mb-motion .mb-listing:not(.is-intro) .mb-listing-addr,
.mb-motion .mb-listing:not(.is-intro) .mb-listing-main > .mb-card-facts,
.mb-motion .mb-listing:not(.is-intro) .mb-gallery-main .mb-pill,
.mb-motion .mb-listing:not(.is-intro) .mb-gallery-count,
.mb-motion .mb-listing:not(.is-intro) .mb-gallery-thumbs li,
.mb-motion .mb-listing:not(.is-intro) .mb-listing-aside{
  opacity:0;animation:mb-intro-failsafe 0s 2.5s forwards}
@keyframes mb-intro-failsafe{to{opacity:1}}

/* Crossfade. The incoming layer fades in OVER the outgoing one, which
   stays opaque underneath (.is-under), so the dissolve never dips
   through the grey background. The drift is 1.5% in the direction of
   travel with a 4% settle: enough to read as movement, not a slide. */
.mb-fade .mb-fade-img{opacity:0;z-index:0;transition:none;
  transform:translateX(var(--mb-drift,0)) scale(1.04)}
.mb-fade .mb-fade-img.is-under{opacity:1;transform:none;z-index:1}
.mb-fade .mb-fade-img.is-front{opacity:1;transform:none;z-index:2;
  transition:opacity .7s cubic-bezier(.16,1,.3,1),
             transform 1.4s cubic-bezier(.16,1,.3,1)}
.mb-fade .mb-fade-img.is-front.is-instant{transition:none}
/* GSAP owns the photo's transform during the intro. */
.mb-gallery-main.is-intro-running .mb-fade-img{transition:none!important}

.mb-gallery-main .mb-pill,.mb-gallery-count{z-index:3}
.mb-gallery-count{font-variant-numeric:tabular-nums}
.mb-gallery-main.is-interactive{cursor:zoom-in}

/* Arrows and the full-screen button. White discs with ink icons: the
   red is kept for the hover and focus state, never for a resting
   control sitting on top of a photograph. */
.mb-gallery-nav,.mb-gallery-expand{position:absolute;z-index:3;
  display:grid;place-items:center;padding:0;border:0;cursor:pointer;
  background:rgba(255,255,255,.92);color:var(--mb-ink);
  box-shadow:0 6px 18px rgba(0,0,0,.18);
  transition:opacity .3s ease,transform .35s cubic-bezier(.16,1,.3,1),
             background-color .2s ease,color .2s ease}
.mb-gallery-nav{top:50%;width:44px;height:44px;margin-top:-22px;
  border-radius:50%}
.mb-gallery-nav svg{width:22px;height:22px}
.mb-gallery-prev{left:.75rem}
.mb-gallery-next{right:.75rem}
.mb-gallery-expand{top:1rem;right:1rem;width:40px;height:40px;
  border-radius:6px}
.mb-gallery-expand svg{width:20px;height:20px}
.mb-gallery-nav:hover,.mb-gallery-expand:hover{background:#fff;
  color:var(--mb-red)}
.mb-gallery-prev:hover{transform:translateX(-2px)}
.mb-gallery-next:hover{transform:translateX(2px)}
.mb-gallery-expand:hover{transform:scale(1.06)}
.mb-gallery-nav:focus-visible,.mb-gallery-expand:focus-visible,
.mb-thumb:focus-visible{outline:3px solid var(--mb-red);outline-offset:2px}
@media(min-width:700px){
  .mb-gallery-nav{width:52px;height:52px;margin-top:-26px}
  .mb-gallery-prev{left:1.25rem}
  .mb-gallery-next{right:1.25rem}
}
/* On a mouse, the controls wait for the pointer so the photograph is
   uncluttered at rest. Touch and keyboard always see them. */
@media(hover:hover) and (pointer:fine){
  .mb-gallery-main .mb-gallery-nav,.mb-gallery-main .mb-gallery-expand{opacity:0}
  .mb-gallery-main:hover .mb-gallery-nav,
  .mb-gallery-main:hover .mb-gallery-expand,
  .mb-gallery-main:focus-within .mb-gallery-nav,
  .mb-gallery-main:focus-within .mb-gallery-expand{opacity:1}
}

/* Thumbnails: the strip recedes and the chosen photo stands forward. */
.mb-gallery-thumbs{position:relative}
.mb-thumb:not(.is-active){opacity:.68}
.mb-thumb:hover{opacity:1}
.mb-thumb img{transition:transform .5s cubic-bezier(.16,1,.3,1)}
.mb-thumb:hover img{transform:scale(1.08)}

/* Key features: each tick pops in just after its line arrives. */
.mb-motion .mb-listing-highlights li[data-mb-reveal] svg{opacity:0;
  transform:scale(.3);
  transition:transform .6s cubic-bezier(.34,1.56,.64,1),opacity .3s ease;
  transition-delay:calc(var(--i,0) * 90ms + 200ms)}
.mb-motion .mb-listing-highlights li.is-in svg{opacity:1;transform:none}

.mb-floorplan img{transition:transform .8s cubic-bezier(.16,1,.3,1)}
.mb-floorplan:hover img{transform:scale(1.02)}
.mb-floorplan-hint{transition:background-color .2s ease}
.mb-floorplan:hover .mb-floorplan-hint{background:var(--mb-red)} /* 7.13:1 */

/* Full-screen viewer. A native <dialog>, so focus is trapped and the
   page behind is inert without any script doing it. */
html.mb-lb-open{overflow:hidden}
.mb-lightbox{position:fixed;inset:0;width:100%;height:100%;
  max-width:none;max-height:none;margin:0;padding:0;border:0;
  background:#0B0B0B;color:#fff;flex-direction:column;
  opacity:0;transition:opacity .3s ease}
.mb-lightbox[open]{display:flex}
.mb-lightbox.is-open{opacity:1}
.mb-lightbox::backdrop{background:rgba(0,0,0,.9)}
.mb-lb-bar{flex:0 0 auto;display:flex;align-items:center;
  justify-content:space-between;padding:.75rem 1rem}
.mb-lb-count{margin:0;font-size:.9375rem;color:#fff;
  font-variant-numeric:tabular-nums}
.mb-lb-close{width:44px;height:44px;display:grid;place-items:center;
  padding:0;border:0;border-radius:50%;cursor:pointer;
  background:rgba(255,255,255,.1);color:#fff;
  transition:background-color .2s ease}
.mb-lb-close:hover{background:rgba(255,255,255,.22)}
.mb-lb-close svg{width:22px;height:22px}
.mb-lb-close:focus-visible{outline:3px solid #fff;outline-offset:2px}
.mb-lb-stage{position:relative;flex:1 1 auto;min-height:0;
  transform:scale(.97);transition:transform .45s cubic-bezier(.16,1,.3,1)}
.mb-lightbox.is-open .mb-lb-stage{transform:none}
.mb-lb-stage img{position:absolute;top:0;left:0;width:100%;height:100%;
  object-fit:contain}
@media(min-width:768px){
  .mb-lb-stage img{left:5.5rem;width:calc(100% - 11rem)}
}
/* On black, the arrows go translucent white rather than the white disc
   used over photographs. */
.mb-lightbox .mb-gallery-nav{background:rgba(255,255,255,.12);color:#fff;
  box-shadow:none}
.mb-lightbox .mb-gallery-nav:hover{background:rgba(255,255,255,.26);color:#fff}
.mb-lightbox .mb-gallery-nav:focus-visible{outline-color:#fff}

@media(prefers-reduced-motion:reduce){
  .mb-fade .mb-fade-img,.mb-fade .mb-fade-img.is-front{transition:none;transform:none}
  .mb-lightbox,.mb-lb-stage{transition:none}
  .mb-lb-stage{transform:none}
  .mb-gallery-nav,.mb-gallery-expand,.mb-thumb img,.mb-floorplan img{transition:none}
  .mb-gallery-prev:hover,.mb-gallery-next:hover,.mb-gallery-expand:hover,
  .mb-thumb:hover img,.mb-floorplan:hover img{transform:none}
}
