/* =============================================================================
   Hamlett Renovations - design system
   Ported from build/reference/hamlett-mock-site_3.html

   _3 is the target revision: it is the only one carrying all nine components,
   and it is the one where the financing block was replaced by a financing FAQ,
   which matches the live site.

   Deliberately NOT ported from the mock:
     .mockbar / .page / .page.live  - the mock's own page-switcher chrome
     .logo-tile and the SVG wordmark - we use the real logo files instead

   Old class names are aliased onto the new ones (.ey with .eyebrow, .btn-blue
   with .btn-primary, and so on) so existing markup picks up the new design
   without being rewritten. Markup changes only where a component genuinely
   needs new structure.
   ========================================================================== */

:root{
  /* brand: navy outline, steel-blue roofs, black tile, white serif wordmark */
  --navy:#132A44;
  --navy-deep:#0C1D31;
  --steel:#5A7E9E;
  /* --steel as TEXT measures 3.96:1 on --paper, just under AA, and it carries
     the eyebrows on 42 pages. Rather than shift the accent colour itself -
     which is also a border and hover colour where contrast does not apply -
     text uses this darker variant. 4.55:1 on paper, 4.92:1 on white. */
  --steel-ink:#4A6E8E;
  --steel-lt:#93AEC5;
  --tile:#14171A;
  --paper:#F7F6F3;
  --paper-2:#EDEBE5;
  --sand:#C6B393;
  --ink:#1B242E;
  --ink-soft:#4E5B68;
  /* WCAG AA. #8B96A1 measured 2.79:1 on --paper and 3.01:1 on white, and it
     carries the 10-12px type - eyebrows, notes, breadcrumbs, captions, table
     labels - which is exactly where contrast matters most. #67727D is the
     smallest darkening that clears 4.5:1 on both, so the muted character of
     the palette is kept. Revert this one line to restore the old tone. */
  --stone:#606B76;
  --rule:#DBD9D2;
  /* pulled from the logo artwork itself */
  --brand-blue:#6F93AF;
  --wordmark-ink:#33322F;

  --display:"Fraunces",Georgia,serif;
  --body:"Jost",system-ui,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,monospace;
  --gut:clamp(18px,5vw,60px);

  /* Interior-page hero scale. 44 pages referenced --fs-hero from a :root block
     that lived in their own inline CSS. When that block went with the old design,
     the variable resolved to nothing and every interior h1 silently fell back to
     body size - 17px, inside a max-width:16ch that made it 180px wide. */
  --fs-hero:clamp(30px,4.4vw,50px);
  /* Section-heading scale. Same failure as --fs-hero above: .faq h2, .brands h2
     and .ctaband h2 kept referencing it after the :root block that defined it was
     dropped, so all three fell back to inherited body size - and .ctaband is on
     every page. One notch under the h2 default, which is what the mock used. */
  --fs-h2:clamp(25px,3.2vw,38px);

  /* compatibility aliases: existing markup still references these names */
  --line:var(--rule);
  --muted:var(--ink-soft);
  /* compatibility alias for the old design; points at the AA-passing variant
     because it is used as TEXT on .pcard-loc, .pcard-go, .tier .meta and .mat .k,
     and as a BACKGROUND under white on .sr-tag and .av - both improve. This is
     NOT --brand-blue, which is the value taken from the logo and is untouched. */
  --blue:var(--steel-ink);
  --skyline:var(--steel-lt);
  --sky-2:var(--paper-2);
  --r:0;                 /* square corners: every radius resolves to nothing */
}

*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{background:var(--paper);color:var(--ink);font-family:var(--body);font-weight:300;font-size:17px;line-height:1.6;-webkit-font-smoothing:antialiased}
a{color:inherit}
img,svg{max-width:100%}
img{height:auto}
:focus-visible{outline:2px solid var(--steel);outline-offset:3px}

.wrap{max-width:1180px;margin:0 auto;padding:0 var(--gut)}
/* Section rhythm floor. The group below names CLASSES, so .babox (73
   sections), .intro (37), .projprose, .tiers, .trustrow and 59 unclassed
   <section>s received no vertical padding at all and their last line ran
   straight into the next block eyebrow. An element selector loses to every
   class rule, so this is a floor rather than an override. */
section{padding:clamp(38px,5vw,72px) 0}
/* .who is NOT in this list. It was, and this is a SECTION padding rule - so
   every review attribution, <div class="who"> inside .rcard, was given 96px of
   padding top and bottom. That is the empty space under three quotes on three
   pages: a one-line byline in a 21px box wearing section padding, 213px tall.

   Nothing uses .who as a section. Checked before removing rather than after: 9
   elements carry the class across the whole site and every one is a byline. */
.band,.svc,.work,.how,.reviews,.brands-band,.towns,.qf,.showroom,.reel{padding:clamp(50px,7vw,96px) 0}
.tone,.sky{background:var(--paper-2)}
.center{text-align:center}

/* ---------- type ---------- */
h1,h2,h3,h4{font-family:var(--display);font-weight:500;line-height:1.08;letter-spacing:-.015em;color:var(--navy)}
h1{font-size:clamp(34px,5.6vw,66px)}
h2,.h2{font-size:clamp(27px,3.9vw,46px);font-family:var(--display);font-weight:500;color:var(--navy);line-height:1.08}
h3{font-size:clamp(19px,2.1vw,25px)}
p{max-width:74ch}
.ey,.eyebrow{font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--steel-ink);display:block}
.lede,.sub,.ssub{font-size:clamp(16px,1.4vw,20px);color:var(--ink-soft);max-width:58ch;font-weight:300}
.center .lede,.center .ssub,.center .sub{margin-left:auto;margin-right:auto}
.note,.upd{font-family:var(--mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--stone);margin-top:11px}
/* .note is a LABEL - mono, uppercase, 10.5px. It reads as a deliberate
   caption on a one-liner and as shouting on a paragraph. Explanatory prose
   under a table or a list uses this instead. */
.fineprint{font-size:14px;color:var(--ink-soft);max-width:62ch;margin-top:16px;line-height:1.6}

/* ---------- buttons: navy fill or navy outline. No gold anywhere. ---------- */
.btn{display:inline-block;font-family:var(--body);font-weight:500;font-size:15px;padding:15px 28px;text-decoration:none;border:1px solid var(--navy);border-radius:0;transition:.18s;cursor:pointer}
/* A button's treatment is decided by the GROUND it sits on, not by the class of
   the wrapper it happens to be inside. Dark components declare these four
   properties once; every button within responds, at any depth, and the rules
   below never name a wrapper. Adding a dark component means declaring it dark -
   not remembering to patch its buttons. */
.btn-primary,.btn-blue,.btn-gold{background:var(--btn-fill,var(--navy));color:var(--btn-ink,#fff);border-color:var(--btn-fill,var(--navy))}
.btn-primary:hover,.btn-blue:hover,.btn-gold:hover{background:var(--btn-fill-hover,var(--steel));border-color:var(--btn-fill-hover,var(--steel));color:var(--btn-ink-hover,#fff)}
.btn-ghost{background:transparent;color:var(--btn-ghost-ink,var(--navy));border-color:var(--btn-ghost-line,var(--navy))}
.btn-ghost:hover{background:var(--navy);color:#fff}
.cta-row{display:flex;flex-wrap:wrap;gap:12px;margin-top:28px}
.center .cta-row{justify-content:center}

/* ---------- credential strip (the one bar above the nav) ---------- */
/* 12px, not 11: this bar carries the licence number and the rating, which are
   trust signals a reader should not have to work at. */
.strip{background:var(--navy-deep);color:#C7D2DD;font-family:var(--mono);font-size:12px;letter-spacing:.06em}
.strip .wrap{display:flex;flex-wrap:wrap;gap:6px 20px;justify-content:space-between;align-items:center;padding-top:9px;padding-bottom:9px}
.strip a{text-decoration:none;display:inline-block;padding:5px 3px;margin:-5px -3px}
.strip a:hover{color:var(--sand)}

/* ---------- nav ---------- */
/* No backdrop-filter: measured 32% more per forced layout, and at 95% opacity
   the blur is invisible anyway. */
.nav{position:sticky;top:0;z-index:80;background:rgba(247,246,243,.97);border-bottom:1px solid var(--rule)}
.wrap.nav,.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:14px;padding-top:14px;padding-bottom:14px}
.brand{display:flex;align-items:center;gap:11px;text-decoration:none;flex:none}
/* the real logo lockup, sized by height. The source is 1607x191, far beyond
   what this needs, so no srcset is required. */
/* 44px, down from 51. The lockup went 1352x191 (7.08) to 1607x191 (8.414) when
   the mark was re-rendered from the screenprint SVG, and the logo is sized by
   HEIGHT - so it is 19% wider at any given height. Measured, not guessed: at
   1024 the bar fits up to 44px and overflows at 48. 44 x 8.414 = 370px, which
   is the 368px the old lockup occupied, so the nav lays out as it did.

   1000-1010px still overflows by ~19px. That is NOT this logo: pinned to the
   old 368px width the same band overflows by 17px, so it is a pre-existing
   floor in the bar itself, and sizing down to 41px to clear it would cost 7% of
   the wordmark to fix an older bug. Left alone deliberately.

   The mark is BIGGER than before despite the smaller height - it is 32% of the
   new canvas against 25% of the old - while the wordmark renders about 12%
   smaller. That is inherent to the new artwork, not to this number. */
.brand .logo-img{height:44px;width:auto;display:block;border-radius:0}
@media(max-width:600px){.brand .logo-img{height:38px}}
/* Below ~430px the logo no longer fits beside the burger: the file is 1607x191,
   so at height:38px it is 320px wide, .brand is flex:none, and at a 320px
   viewport the inner width is only 284px. Every page scrolled sideways there.
   Taper the width instead of the height so it only engages when it must -
   at 430px this computes to 318px, i.e. no visible step. */
@media(max-width:430px){
  .brand{flex:0 1 auto;min-width:0}
  /* The cap is the width the logo occupies at its mobile height:
     1607x191 is an 8.414 ratio, so 38 x 8.414 = 320px. Recompute this if the
     file ever changes - at 7.24 it was 319px, and the 1400x240 screenprint
     lockup (5.83) needed 257. Left at the wrong value the logo renders TALLER
     below 430px than the height it is tapering down from. */
  .brand .logo-img{height:auto;width:min(320px,calc(100vw - 112px));max-width:100%}
}

.links{display:none;align-items:center;gap:24px;font-size:14.5px}
.links a{color:var(--ink-soft);text-decoration:none;font-weight:400;padding:6px 0;border-bottom:2px solid transparent;white-space:nowrap}
.links a:hover{color:var(--navy)}
.links a.active,.links a:hover,.links a.on{color:var(--navy)}
.links a.active{border-bottom-color:var(--steel-ink)}
.navcall{font-family:var(--mono);font-size:12px;letter-spacing:.06em;color:var(--navy);text-decoration:none;white-space:nowrap}
.navcall:hover{color:var(--steel)}
@media(min-width:1000px){.links{display:flex}}

/* Services dropdown. Opens on hover and on keyboard focus, no JavaScript. The
   menu starts hidden, which would normally make its links unfocusable - it does
   not, because focusing the parent "Services" link satisfies :focus-within and
   reveals the menu, making its children focusable in turn. */
.navdrop{position:relative;display:inline-flex;align-items:center}
.navdroptop{display:inline-flex;align-items:center;gap:6px}
.navcaret{width:9px;height:6px;transition:transform .18s ease}
.navdrop:hover .navcaret,.navdrop:focus-within .navcaret{transform:rotate(180deg)}
.navdrop::after{content:"";position:absolute;top:100%;left:0;right:0;height:12px}
.navmenu{position:absolute;top:calc(100% + 12px);left:-16px;min-width:232px;display:block;padding:8px 0;background:var(--paper);border:1px solid var(--rule);box-shadow:0 12px 28px rgba(19,42,68,.1);opacity:0;visibility:hidden;pointer-events:none;transition:opacity .16s ease;z-index:90}
.navdrop:hover .navmenu,.navdrop:focus-within .navmenu{opacity:1;visibility:visible;pointer-events:auto}
.links .navmenu a{display:block;padding:9px 18px;font-size:14.5px;border-bottom:0;white-space:nowrap}
.links .navmenu a:hover,.links .navmenu a.active{background:var(--paper-2);color:var(--navy)}

.burger{display:grid;place-items:center;width:44px;height:44px;background:none;border:1px solid var(--rule);color:var(--navy);cursor:pointer;border-radius:0}
.burger svg{width:22px;height:22px}
@media(min-width:1000px){.burger{display:none}}
.mnav{display:none;flex-direction:column;background:var(--paper);border-top:1px solid var(--rule)}
.mnav.open{display:flex}
.mnav a{padding:14px var(--gut);border-bottom:1px solid var(--rule);text-decoration:none;color:var(--ink-soft);font-size:15px}
.mnav a.active{color:var(--navy)}
@media(min-width:1000px){.mnav,.mnav.open{display:none}}

/* ---------- proof strip ---------- */
.proof{border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:24px 0;background:var(--paper-2)}
.proof .wrap{display:flex;flex-wrap:wrap;gap:12px 40px;justify-content:space-between;align-items:baseline}
.pitem{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-soft)}
.pitem b{font-family:var(--display);font-size:25px;font-weight:600;display:block;letter-spacing:0;text-transform:none;color:var(--navy)}

/* ---------- service tiles ---------- */
.tiles-lead,.svcgrid.svclead{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:38px}
.tiles-rest,.svcgrid.svcrest{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-top:16px}
@media(max-width:820px){.tiles-lead,.tiles-rest,.svcgrid.svclead,.svcgrid.svcrest{grid-template-columns:1fr 1fr}}
@media(max-width:520px){.tiles-rest,.svcgrid.svcrest{grid-template-columns:1fr}}
.tile,.svccard{display:block;text-decoration:none;border:1px solid var(--rule);background:#fff;border-radius:0;box-shadow:none;transition:.18s}
.tile:hover,.svccard:hover{border-color:var(--steel);transform:translateY(-2px);box-shadow:none}
.tile-img,.svcimg{aspect-ratio:16/10;overflow:hidden;background:var(--paper-2);display:block}
.tiles-rest .tile-img,.svcgrid.svcrest .svcimg{aspect-ratio:4/3}
.tile-img img,.svcimg img{width:100%;height:100%;object-fit:cover;transition:transform .4s ease}
.tile:hover .tile-img img,.svccard:hover .svcimg img{transform:scale(1.04)}
.tile-body,.svcbody{padding:18px 20px 22px;text-align:left}
.tile-body h3,.svcbody h3{margin-bottom:5px;font-size:20px}
.tiles-rest .tile-body h3,.svcgrid.svcrest .svcbody h3{font-size:17px}
.svcgrid.svclead .svcbody h3{font-size:24px}
.tile-body p,.svcbody p{font-size:14.5px;color:var(--ink-soft);margin-bottom:0}
.tile-more,.svclink{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--steel-ink);margin-top:12px;display:block}

/* ---------- dark bands ---------- */
.dark{background:var(--navy);color:#D7E0E9}
.dark h1,.dark h2,.dark h3{color:#fff}
.dark .lede,.dark .sub,.dark .ssub,.dark p{color:#B7C5D2}
.dark .ey,.dark .eyebrow{color:var(--sand)}
.dark .btn-ghost{color:#fff;border-color:#fff}
.dark .btn-ghost:hover{background:#fff;color:var(--navy)}
.split,.srgrid{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr);gap:clamp(26px,4.5vw,56px);align-items:center}
/* The investment split is a figure beside a list, and they are never the same
   height - centring pushed the shorter column down 75px on /kitchen-remodeling,
   which reads as a mistake rather than as a choice. Both pages use #investment. */
#investment .split{align-items:start}
@media(max-width:860px){.split,.srgrid{grid-template-columns:1fr}}
.checklist{list-style:none;margin-top:20px;display:grid;gap:11px}
.checklist li{padding-left:24px;position:relative;font-size:15.5px;color:#C3D0DB}
.checklist li:before{content:"\2014";position:absolute;left:0;color:var(--sand);font-family:var(--mono)}
.card{border:1px solid #35506D;padding:28px;background:rgba(255,255,255,.03);border-radius:0}
.card p{color:#B7C5D2;font-size:15.5px}
/* .card is written for a .dark band, where the ground comes from the band.
   The homepage's plan-review card sits in a light section, so it carries the
   ground itself. p and .pprice are already handled by .card above. */
.card.on-light{background:var(--navy);border-color:#35506D}
.card.on-light h3{color:#fff}
.card.on-light .ey{color:var(--sand)}
/* The component sets a navy ground and recolours h3 and .ey, but never p - so a
   paragraph inside it inherited the light-ground body ink and measured 2.09:1.
   Same shape as .vwalk .inlink: a dark-ground component missing one element. */
.card.on-light p{color:#C6D2DE}
.card.on-light strong,.card.on-light b{color:#fff}

/* ---------- inline text link ---------- */
/* Underlined rather than the old blue-and-bold: site.css sets a{color:inherit},
   so colour alone was doing all the work and 11 pages that never carried the
   page-level copy had links that read as plain prose. An underline survives a
   missing colour. */
.inlink{color:var(--navy);font-weight:500;text-decoration:underline;text-decoration-color:var(--steel-lt);text-decoration-thickness:1px;text-underline-offset:3px}
.inlink:hover{text-decoration-color:var(--navy)}
/* Every component that paints a dark ground, in one place. A button anywhere
   inside one of these inverts automatically - this is the list to extend when a
   new dark component appears, and btnvis.html fails the build if it is missed.
   Derived by MEASUREMENT, not memory: darkgrounds.html walks all 47 routes and
   reports every element painting luminance<0.22 that contains a .btn. Writing
   this list from memory the first time missed .gcta on the two guide pages.
   section.vhero is deliberately absent - its buttons sit over photography, not
   over its background-color. */
section.band:not([style]),.ctaband,.dark,.planband,.card.on-light,.gcta,.abhero{
  --btn-fill:#fff;--btn-ink:var(--navy);
  --btn-fill-hover:var(--sand);--btn-ink-hover:var(--navy);
  --btn-ghost-ink:#fff;--btn-ghost-line:rgba(255,255,255,.55)}
.dark .inlink,.card .inlink,.ctaband .inlink,.planband .inlink{color:#fff;text-decoration-color:var(--sand)}
.dark .inlink:hover,.card .inlink:hover,.ctaband .inlink:hover{text-decoration-color:#fff}

/* ---------- three ranges stacked ---------- */
/* .range is one big figure. Three of them at clamp(32px,4.6vw,54px) would be a
   wall, so this is a ledger: label left, figure right, rule between. The figure
   keeps the display face so it still reads as the number that matters. */
.rangelist{list-style:none;margin:18px 0 0;display:grid;gap:0}
.rangelist li{display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  padding:14px 0;border-bottom:1px solid var(--rule)}
.rangelist li:first-child{border-top:2px solid var(--navy)}
.rangelist li>span{font-family:var(--mono);font-size:11.5px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--stone)}
.rangelist li>b{font-family:var(--display);font-weight:500;line-height:1;color:var(--navy);
  font-size:clamp(24px,2.9vw,34px);letter-spacing:-.01em;white-space:nowrap}

/* ---------- proof bar, one thin line ---------- */
/* The stat-tile version (.pitem, 25px figures) reads as a section. Between a
   hero and a video it should be a rule with words on it, so this variant drops
   the tiles and sets everything on one baseline with dot separators. */
.proof.proofline{padding:0;background:var(--paper)}
.proof.proofline .wrap{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:0 clamp(10px,1.6vw,20px);padding-top:13px;padding-bottom:13px;
  font-family:var(--mono);font-size:11px;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-soft)}
.proof.proofline .pl{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.proof.proofline .pl b{font-weight:500;color:var(--navy)}
.proof.proofline .pl-sep{color:var(--rule)}
/* inline-block first: padding on an inline <a> does not grow its hit box,
   and 17px tall is a miss on a phone (the audit wants 24) */
.proof.proofline a{color:inherit;text-decoration:none;border-bottom:1px solid transparent;
  display:inline-block;padding:5px 1px;line-height:1.35}
.proof.proofline a:hover{border-bottom-color:var(--steel)}
/* Below 560px the separators are what breaks the line badly - the items wrap
   fine on their own, the dots strand at the start of a row. */
@media(max-width:559px){.proof.proofline .pl-sep{display:none}
  .proof.proofline .wrap{gap:6px 14px;font-size:10px}}

/* ---------- four-step process ---------- */
/* .phases is baked to 3 columns for the service pages' design phase. The
   homepage process is four steps, so this is a variant, not an edit. */
.phases.phases-4{grid-template-columns:repeat(4,1fr)}
@media(max-width:1100px){.phases.phases-4{grid-template-columns:repeat(2,1fr)}
  .phases.phases-4 .phase:nth-child(2n){border-right:none}
  .phases.phases-4 .phase:nth-child(-n+2){border-bottom:1px solid var(--rule)}}
@media(max-width:860px){.phases.phases-4{grid-template-columns:1fr}}

/* ---------- plan review, as a band ---------- */
/* Kept from the deleted showroom section because it is a real lead source, but
   demoted: a band between two sections rather than a card inside one. */
.planband{background:var(--navy);color:#fff;padding:clamp(26px,3.4vw,38px) 0}
.planband .wrap{display:grid;gap:clamp(16px,2.6vw,38px);align-items:center}
@media(min-width:860px){.planband .wrap{grid-template-columns:minmax(0,1fr) auto}}
.planband .ey{color:var(--sand);margin-bottom:8px}
.planband h3{color:#fff;font-size:clamp(20px,2.2vw,26px);margin:0 0 8px}
.planband p{color:#C6D2DE;font-size:15.5px;margin:0;max-width:66ch}
.planband .pprice{color:var(--sand);border-top-color:#35506D;display:inline-block;margin-top:14px}
.planband .btn-ghost{color:#fff;border-color:#fff;white-space:nowrap}
.planband .btn-ghost:hover{background:#fff;color:var(--navy)}

/* ---------- interior hero with a summary card ---------- */
/* Two columns so the at-a-glance card sits beside the copy. Below 1000px it
   stacks under it and the hero simply grows - the card is the page's summary
   (price, timeline, warranty), so it earns the height rather than being hidden. */
@media(min-width:1000px){
  /* centre, not end: the card is 501px and the copy block 198px, so bottom-
     aligning them stacks the whole 303px difference above the copy. Centring
     splits it, and a symmetric offset reads as intended.
     348px, not 330: "Bergen, Passaic, Morris & Essex" needs 203px and had 198. */
  .shero-bleed .shero-inner{display:grid;grid-template-columns:minmax(0,1fr) 348px;
    gap:clamp(28px,4vw,56px);align-items:center}
  .shero-bleed .shero-inner > .head{max-width:none}
}
/* .sidecard is sky-blue on white by default. Over a video that is a light slab
   on a dark frame, so this is the same card in the hero's own register. */
/* .86 not .72. The card sits over photographs that change page to page, so the
   ground is not ours to control - only the card is. At .72 the --steel-lt labels
   composited to 2.94:1 over a light photo, under AA on six pages. Solved against
   the WORST case, the card over pure white: .84 lands exactly on 4.50 and .86
   gives 4.83, which is the margin worth having when the real number depends on
   a photo somebody may swap next month. 14% of the image still reads through. */
.shero-bleed .sidecard{background:rgba(12,29,49,.86);border:1px solid rgba(255,255,255,.22);
  border-top:3px solid var(--sand);backdrop-filter:blur(3px);color:#fff}
/* h2 not h3: in the hero this card follows the h1 directly, and h1->h3
   skips a level. It keeps the h3 SIZE - the level is about document structure,
   the size is about the card. Elsewhere .sidecard sits under a section h2, so
   there h3 is still correct and untouched. */
.shero-bleed .sidecard h2,.shero-bleed .sidecard h3{color:#fff}
.sidecard .sidecard-h{font-size:18px;margin-bottom:10px}
.shero-bleed .sidecard .li{border-bottom-color:rgba(255,255,255,.16)}
.shero-bleed .sidecard .li span:first-child{color:var(--steel-lt)}
.shero-bleed .sidecard .li span:last-child{color:#fff}
.shero-bleed .sidecard .included{border-top-color:rgba(255,255,255,.22);color:var(--steel-lt)}
.shero-bleed .sidecard .included strong{color:#fff}
@media(max-width:999px){.shero-bleed .sidecard{margin-top:28px;max-width:420px}}

/* ---------- a gallery with one photograph in it ---------- */
/* Only one finished basement has been photographed. A single figure dropped
   into a three-track grid sits in the left third looking like two are missing,
   so this centres it and gives it room instead of pretending to a row. */
.grid3.grid3-one{grid-template-columns:minmax(0,760px);justify-content:center}
/* Same reason, two photographs: a pair sitting in a three-track grid reads
   as a row with one missing. */
.grid3.grid3-two{grid-template-columns:repeat(2,minmax(0,1fr));justify-content:center;max-width:820px;margin-inline:auto}
.grid3.grid3-one .proj img{width:100%;height:auto;aspect-ratio:4/3}

/* ---------- index card grid ---------- */
/* Was defined page-inline on /services AND /projects, byte-identical, so a fix
   to one silently left the other broken - which is how /services kept an
   unequal-row-height bug that /projects had already had fixed. One definition.

   grid-auto-rows:1fr because the grid stretches each ROW independently: six
   cards over two rows gave a 222px row above a 200px row at every desktop
   width. The tallest card now sets the height for all of them. */
.gindex{max-width:900px;margin:0 auto;padding:40px 26px;display:grid;gap:18px;
  grid-template-columns:repeat(auto-fit,minmax(260px,1fr));grid-auto-rows:1fr}
.gcard{display:block;border:1px solid var(--line);border-radius:0;padding:22px 24px;
  text-decoration:none;color:inherit;transition:box-shadow .15s,transform .15s}
.gcard:hover{box-shadow:none;transform:translateY(-2px)}
.gcard h2{margin:0 0 6px;font-size:1.25rem}
.gcard p{margin:0;color:var(--muted)}
/* a card is narrower than a section, and "Commercial - Mountainside, NJ" does
   not fit at the tracking eyebrows use elsewhere */
.gcard .ey{white-space:nowrap;letter-spacing:.05em}
/* A label above the grid has to share the grid box, not the page wrap: .gindex
   is 900px centred while .wrap is 1180px centred, so an eyebrow in .wrap sat
   108px left of the cards it was labelling. */
.gindex-head{max-width:900px;margin:0 auto;padding:0 26px}

/* ---------- the trust line under a hero ---------- */
/* Was defined page-inline on 43 pages, byte-identical, and nowhere in this
   file. /about used the markup without carrying the rule and got three
   undifferentiated inline spans. One definition. */
.mini{display:flex;flex-wrap:wrap;gap:8px 16px;margin-top:20px;font-size:13.5px;
  color:var(--muted);font-weight:600}
.mini span{display:inline-flex;align-items:center;gap:6px}

/* ---------- the /about band ---------- */
/* Not a photograph. /about is the page people open right before they call, and
   a blurred countertop corner says nothing they came for. A flat navy band with
   the house mark on it is closer to a letterhead than a hero, which is the right
   register for the page that answers "who are you".

   The mark is /assets/hamlett-mark.png - the house already cropped out of the
   nav lockup, 335x191, in the repo since before this work. No new asset, and no
   cropping needed.

   Buttons are not styled here. .abhero is in the dark-ground list above, so it
   declares --btn-fill and every button inside adapts at any depth. */
/* tuned to the 45vh target */
.abhero{background:var(--navy);min-height:45vh;display:flex;align-items:center;
  padding:clamp(26px,3.2vw,42px) 0}
.abhero-in{width:100%;display:grid;gap:clamp(22px,3.5vw,56px);align-items:center;
  grid-template-columns:minmax(0,1fr)}
/* Two columns only when there is room for both. Below this the mark drops to
   its own row, which is how "clear of the text at every width" is guaranteed by
   construction rather than by choosing a size and hoping. */
@media(min-width:860px){.abhero-in{grid-template-columns:minmax(0,1fr) auto}}
.abhero h1{color:var(--paper);max-width:none;margin:10px 0 14px;
  font-size:clamp(30px,3.6vw,50px);line-height:1.06}
.abhero .sub{color:#C6D2DE;max-width:62ch}
.abhero .crumb{color:var(--steel-lt);padding-top:0}
.abhero .crumb a{color:var(--steel-lt)}
.abhero .mini{color:var(--steel-lt)}
.abhero .cta{margin-top:18px}
/* the rule and the counties, under the sub-line */
.abhero-rule{width:56px;height:2px;background:var(--steel-lt);margin:18px 0 10px;border:0}
.abhero-area{font-family:var(--mono);font-size:12px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--steel-lt)}
/* about a third of the band: 45vh band, 15vh mark */
.abhero-mark{height:clamp(64px,15vh,152px);width:auto;display:block;justify-self:end;opacity:.92}
/* Hidden below the two-column breakpoint. Stacked, the mark cost a whole row
   and pushed the band to 71% of a phone viewport - a lot of navy before anyone
   sees content, for a decorative element. aria-hidden already, so nothing is
   lost to a screen reader either. */
@media(max-width:859px){.abhero-mark{display:none}}

/* ---------- a hero carried by type alone ---------- */
/* /projects opens on nothing but words. The page under it is entirely finished
   work, so a hero photograph would be the same argument made twice - and the
   one picture chosen would be competing with the forty below it.

   Sized past everything else deliberately: base h1 caps at 66px and --fs-hero
   at 50px, and neither reads as "carrying the screen". 116px does. The floor is
   48px rather than the usual 34 because a short headline at a small size on an
   empty ground reads as a mistake rather than a choice.

   min-height is 40vh, not 100: the point is a clear opening, not a screen of
   nothing to scroll past. */
.thero{background:var(--paper);min-height:40vh;display:flex;align-items:center;
  padding:clamp(30px,4vw,52px) 0 clamp(34px,4.6vw,58px)}
.thero h1{font-size:clamp(48px,9.5vw,116px);line-height:.96;letter-spacing:-.022em;
  color:var(--navy);max-width:none;margin:12px 0 18px;text-wrap:balance}
/* .wrap is a FLEX ITEM here, and flex items are shrink-to-fit. Without this it
   collapsed to the width of its own text - 659px instead of 1180 - and then
   margin:0 auto centred it, putting the headline 321px right of the gallery
   column directly below it. The section reads as centred by accident. Same trap
   as .wrap.prose: a max-width on an auto-margin box MOVES it as well as
   narrowing it. */
.thero .wrap{width:100%}
.thero .crumb{padding-top:0}
.thero-sub{font-size:clamp(17px,1.6vw,21px);line-height:1.5;color:var(--muted);
  max-width:44ch;margin:0}
@media(max-width:600px){.thero{min-height:0}}

/* ---------- a text-only section ---------- */
/* A 585px paragraph (.lead is 56ch, which is a good measure) inside a 1180px
   wrap leaves half the row empty and reads as a hole. The answer is not a wider
   measure - 110 characters a line is worse to read - it is a narrower column.
   Only sections that are prose and nothing else use this; anything with cards
   or a table keeps the full wrap. */
/* Cap the CHILDREN, not the wrap. Narrowing the wrap itself centres it, so a
   780px prose column started 200px right of every full-width section above and
   below it and the page lost its left edge. Capping children keeps the wrap
   where it is and the text block aligned with everything else. */
.wrap.prose > *{max-width:660px}

/* A capped .lead inside a centred container was pinned to the container's LEFT
   edge: max-width with no auto margins. So the text was centred inside a box
   that was not, and it sat 227px (/about) to 238px (service pages) left of the
   heading centred above it. 44 paragraphs across 35 routes.

   Centring the BOX is the fix, and it is safe for a one-liner. Left-aligning
   the TEXT is not - 42 of those 44 are one or two lines, and a short line
   flushed left inside a centred box hangs off its left edge. That is why the
   alignment half is a separate opt-in class and not part of this rule.

   .center .lead is (0,2,0) and beats the page-inline .lead (0,1,0) whatever the
   source order - which matters, because .lead has no rule in site.css and is
   declared inside most pages in three different variants. */
.center .lead{margin-left:auto;margin-right:auto}
/* Opt-in: for a lead long enough that centred text is hard to read. Four lines
   is about where it starts to cost more than it gains. Pair it with the rule
   above, never alone - left text in a left-pinned box is the original defect
   with extra steps. */
.center .lead.lead-left{text-align:left}

/* ---------- when the design phase does not apply ---------- */
/* Sits directly under the design phase and qualifies it. Light, not a navy
   band: the message is that a smaller job is welcome, and a heavy treatment
   reads as a warning rather than an invitation. The sand rule ties it to the
   section above without repeating that section's weight. */
.condnote{border-left:3px solid var(--sand);background:var(--paper-2);
  padding:clamp(22px,2.6vw,30px) clamp(20px,2.4vw,34px);margin-top:clamp(30px,3.6vw,48px)}
/* h2 now, not h3 - it is a section-level heading beside its neighbours, and
   keeps the size it had. */
.condnote h3,.condnote .condnote-h{font-size:clamp(19px,2vw,23px);color:var(--navy);margin:0 0 10px}
.condnote .ey{margin-bottom:8px}
.condnote p{color:var(--ink-soft);font-size:16.5px;line-height:1.65;margin:0;max-width:74ch}

/* ---------- cabinetry and surfaces ---------- */
/* One section that was four. The subheads are h3 with a rule above so they read
   as parts of one argument, not as four separate pitches under four h2s. */
.cabsurf-h{font-size:var(--fs-h2);line-height:1.12;color:var(--navy);
  margin:clamp(38px,4.6vw,64px) 0 14px;padding-top:clamp(22px,2.6vw,34px);
  border-top:1px solid var(--rule)}
.cabsurf-lede .cabsurf-h{margin-top:6px;padding-top:0;border-top:0}
/* The cabinetry argument came out of .intro, and ".intro p" is a page-inline
   rule - so on the way over the paragraphs lost their colour, their 16px and,
   most visibly, their margin-bottom, running together with no gap. Same defect
   Shawn reported in the old section 2. Restated here for the section they now
   live in. */
.cabsurf-lede p{color:var(--muted);font-size:16px;margin-bottom:12px}
.cabsurf-lede .checks{margin-top:16px}
.cabsurf .tier,.cabsurf .mat{text-align:left}
.cabsurf .grid,.cabsurf .mats{margin-top:24px}
.cabsurf-lines{margin-top:22px}
.cabsurf-lines .brandrow{justify-content:flex-start}
.cabsurf .lead{margin-bottom:0}

/* ---------- service hub hero ---------- */
/* The six service hubs carry a longer name than a town page does. At 1.05/.95
   the copy column is 529px - and between 900 and 1180 it is narrower still, which
   is short of what the hub names need at the hero size. The split widens here
   only; 1.4/.85 was chosen because it clears the whole 900-1900 band, not just
   1440. .svchero keeps the 28 town heroes
   at their existing proportions. max-width:none undoes the page-inline 16ch
   clamp - (0,2,1) beats the (0,1,1) of .shero h1 whatever the load order. */
@media(min-width:900px){.shero.svchero .wrap.grid{grid-template-columns:minmax(0,1.4fr) minmax(0,.85fr)}}
.shero.svchero h1{max-width:none}
/* The hub names are longer than a town name. Below ~405px the 30px floor of
   --fs-hero puts the longest two onto a second line by 3-7px, which reads as an
   orphaned word rather than a deliberate break. The floor tapers here only, so
   the 28 town heroes keep the shared scale. */
@media(max-width:480px){.shero.svchero h1{font-size:clamp(26px,7.4vw,30px)}}
/* same taper for the bleed hero: /home-additions carries a hub-length name
   and is no longer a .svchero, so it would otherwise lose it. */
@media(max-width:480px){.shero.shero-bleed h1{font-size:clamp(26px,7.4vw,30px)}}

/* ---------- interior hero with full-bleed media behind it ---------- */
/* A scoped variant: .shero is on 44 pages and none of the others change.
   Same anatomy as .vhero on the homepage - poster paints, video on top of it,
   scrim between the media and the copy. */
.shero-bleed{position:relative;display:flex;align-items:flex-end;overflow:hidden;
  background:var(--navy-deep);min-height:min(70vh,700px);
  padding:0 0 clamp(34px,5vw,58px)}
.shero-media{position:absolute;inset:0;z-index:0}
.shero-media img,.shero-media video{width:100%;height:100%;object-fit:cover;object-position:center 55%;display:block}
.shero-media video{position:absolute;inset:0}
.shero-scrim{position:absolute;inset:0;z-index:1;background:
  linear-gradient(180deg,rgba(12,29,49,0) 0%,rgba(12,29,49,.10) 40%,rgba(12,29,49,.78) 78%,rgba(12,29,49,.94) 100%),
  linear-gradient(90deg,rgba(12,29,49,.52) 0%,rgba(12,29,49,.14) 52%,rgba(12,29,49,0) 100%)}
/* Below 900px the copy spans the full width, so a left-to-right scrim leaves the
   end of each line over the bright side. The copy sits at the bottom, so mobile
   gets a vertical scrim that is darkest there. */
@media(max-width:899px){.shero-scrim{background:linear-gradient(180deg,rgba(12,29,49,.10) 0%,rgba(12,29,49,.34) 40%,rgba(12,29,49,.88) 74%,rgba(12,29,49,.96) 100%)}}
.shero-inner{position:relative;z-index:2;width:100%;padding-top:clamp(150px,26vw,320px)}
.shero-bleed .head{max-width:100%}
@media(min-width:900px){.shero-bleed .head{max-width:min(70%,760px)}}
.shero.shero-bleed h1{color:#fff;max-width:none;margin:10px 0 20px}
.shero-bleed .crumb{color:var(--steel-lt);padding-top:0}
.shero-bleed .crumb a{color:var(--steel-lt)}
/* .upd is the same slot as .sub - a line under the h1 - and /services is the one
   bleed hero that uses it. Without this it renders --stone, a colour picked for
   a light ground: 3.13:1 against the navy, under AA, and worse over a bright
   photograph. Invisible to the contrast check because a bleed hero resolves to
   over-media and is not measured at all. Surfaced by simulating a failed hero
   image, which is not where the bug lives - it is live on the page today. */
.shero-bleed .sub,.shero-bleed .upd{color:#C6D2DE}
.shero-bleed .mini{color:var(--steel-lt)}
.shero-bleed .btn-ghost{color:#fff;border-color:#fff}
.shero-bleed .btn-ghost:hover{background:#fff;color:var(--navy)}
/* the whole label: what it is and where, nothing else */
.shero-tag{position:absolute;z-index:2;right:clamp(16px,4vw,60px);top:clamp(84px,11vw,120px);
  font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;
  color:#fff;background:rgba(12,29,49,.62);border:1px solid rgba(255,255,255,.28);
  padding:7px 12px}
@media(max-width:899px){.shero-tag{top:16px;right:16px;font-size:10px;padding:6px 10px}}

/* ---------- framed walkthrough ---------- */
/* Deliberately NOT the hero. The two bands measured identical on 7 of 11
   signature properties while both were full-bleed dark with white serif copy
   over the media, which is one component appearing twice however different the
   sentences are. Light ground, framed 16:9 media, copy beside it and on the
   opposite side. Nothing here needs a scrim, so the copy uses the ordinary
   light-ground tokens and is legible by construction rather than by tuning. */
.vwalk{background:var(--paper-2)}
.vwalk-grid{display:grid;gap:clamp(24px,4vw,54px);align-items:center}
@media(min-width:900px){.vwalk-grid{grid-template-columns:minmax(0,1.02fr) minmax(0,.98fr)}}
.vwalk-frame{position:relative;margin:0;border:1px solid var(--rule);background:var(--navy-deep);
  overflow:hidden;aspect-ratio:16/9}
.vwalk-media{position:absolute;inset:0}
.vwalk-media img,.vwalk-media video{width:100%;height:100%;object-fit:cover;display:block}
.vwalk-media video{position:absolute;inset:0}
.vwalk h2{margin:12px 0 14px}
.vwalk .lead{color:var(--ink-soft)}
.vwalk-claim{color:var(--navy);font-size:clamp(15px,1.5vw,17px);line-height:1.55;margin-top:16px;
  padding-left:16px;border-left:2px solid var(--sand);max-width:56ch}
.vwalk-cap{font-family:var(--mono);font-size:11.5px;letter-spacing:.05em;color:var(--stone);
  margin-top:22px;line-height:1.9;max-width:60ch}
.vwalk-cap b{color:var(--navy);font-weight:500}
/* the label sits on the frame, not on the page */
.vwalk-tag{position:absolute;z-index:2;left:0;bottom:0;
  font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;
  color:#fff;background:rgba(12,29,49,.82);padding:7px 12px}

/* ---------- framed video ---------- */
/* width/height on the element plus aspect-ratio here means the box is the
   right shape before the poster has decoded, so nothing jumps. */
.vfig{margin:28px 0 0}
.vfig .vplayer{display:block;width:100%;height:auto;aspect-ratio:16/9;background:var(--tile);border:1px solid var(--rule)}
.vfig figcaption{font-size:14px;color:var(--ink-soft);margin-top:12px;max-width:70ch;line-height:1.6}

/* ---------- phase cards ---------- */
.phases{display:grid;grid-template-columns:repeat(3,1fr);margin-top:44px;border-top:2px solid var(--navy)}
@media(max-width:860px){.phases{grid-template-columns:1fr}}
/* column + margin-top:auto so the price line sits on the bottom edge of every
   card regardless of how long the copy above it runs. The mock gets away
   without this because its three cards happen to carry similar copy. */
.phase{padding:28px 26px 32px;border-right:1px solid var(--rule);display:flex;flex-direction:column}
.phase .pprice{margin-top:auto}
.phase:last-child{border-right:none}
@media(max-width:860px){.phase{border-right:none;border-bottom:1px solid var(--rule)}.phase:last-child{border-bottom:none}}
.pno{font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;color:var(--steel);display:block;margin-bottom:16px}
.phase p{font-size:15.5px;color:var(--ink-soft);margin-top:8px}
.pprice{font-family:var(--mono);font-size:11.5px;letter-spacing:.05em;margin-top:15px;padding-top:13px;border-top:1px dashed var(--rule);color:var(--navy)}
.dark .pprice,.card .pprice{color:var(--sand);border-top-color:#35506D}

/* ---------- recovered layout ---------- */
/* Dropped in the port because each class name also appears in a SHARED
   selector (see the section-padding group above), so a class-presence check
   read them as "styled by the design system". Found by diffing properties
   against the pre-port originals, confirmed by measuring at 1440. */

/* /about: the service card grid and the review card grid. Element-scoped so
   the homepage's <section class="svc"> / <section class="reviews"> keep their
   padding and do not become grids. */
div.svc{display:grid;grid-template-columns:1fr;gap:14px;margin-top:32px;padding:0}
@media(min-width:620px){div.svc{grid-template-columns:repeat(2,1fr)}}
@media(min-width:920px){div.svc{grid-template-columns:repeat(3,1fr)}}
div.reviews{display:grid;grid-template-columns:1fr;gap:18px;margin-top:32px;text-align:left;padding:0}
@media(min-width:620px){div.reviews{grid-template-columns:repeat(3,1fr)}}

/* the three cabinetry tiers and the three deck options, both stacked */
/* .cabsurf is on the list because the three tier cards moved into the merged
   cabinetry section; the grid was scoped to .tiers, so they stacked. */
.tiers .grid,.opts .grid,.cabsurf .grid{display:grid;grid-template-columns:1fr;gap:18px;margin-top:32px;text-align:left}
@media(min-width:760px){.tiers .grid,.opts .grid,.cabsurf .grid{grid-template-columns:repeat(3,1fr)}}

/* the photo caption on 36 pages had lost its overlay and dropped below the
   image. Restated in the new language rather than the old navy pill. */
figure.proj{position:relative}
.proj .cap{position:absolute;left:0;right:0;bottom:0;background:linear-gradient(0deg,rgba(19,42,68,.82),transparent);color:#fff;font-family:var(--mono);font-size:12px;letter-spacing:.09em;text-transform:uppercase;padding:26px 14px 12px}
.pgrid .proj img{width:100%;aspect-ratio:4/3;object-fit:cover}
.proj.wide img{width:100%;height:auto}

/* the hero star rating ran its stars into its count - gap was inert without a
   flex display */
.hero-rating{display:inline-flex;align-items:center;gap:9px}

/* the two-column project card on /kitchen-remodeling and
   /commercial-construction. Both pages use the identical inner structure. */
/* The row, not the image, is what needs constraining. Sizing the photo
   instead produced either a 623px card (image at its natural ratio driving
   the row) or a 139px gap under a short photo (aspect-ratio winning over
   stretch on the narrower two-up card). A minimum row height lets the image
   fill whatever the row turns out to be, and the row is the taller of that
   minimum and the copy. */
a.pcard{display:grid;grid-template-columns:1fr;grid-template-rows:minmax(220px,auto)}
@media(min-width:640px){a.pcard{grid-template-columns:minmax(0,44%) minmax(0,1fr);grid-template-rows:minmax(280px,auto)}}
/* The photo had no height constraint, so it rendered at its natural ratio -
   466x621 - and dragged the card to 623px to hold 136px of copy. The text was
   already centred; the card was the problem. 4/3 is the ratio .pcard .ph and
   .proj img already use. If the copy is ever taller than the photo the row
   grows and object-fit:cover takes care of the crop. */
/* The photo is taken out of flow so it contributes NO intrinsic height. With
   height:100% on an auto row it resolved to its natural 621px and dragged the
   card there - the same circularity whichever way round it was written. Out of
   flow, the row is simply the taller of the minimum and the copy, and the
   photo fills whatever that turns out to be. */
.pcard-img{position:relative;overflow:hidden;background:var(--paper-2)}
.pcard-img img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.pcard-body{padding:20px 22px;display:flex;flex-direction:column;gap:8px;justify-content:center}

/* the project video, and its caption - which must stay static. Declared after
   .proj .cap because the two have equal specificity. */
.projvid{margin:22px auto 6px;max-width:340px}
.projvid video{width:100%;height:auto;display:block;background:var(--navy)}
.projvid .vid-still{display:none;width:100%;height:auto}
.projvid .cap{display:block;position:static;background:none;color:var(--ink-soft);font-family:var(--body);font-size:13px;letter-spacing:0;text-transform:none;padding:10px 2px 0;text-align:center;font-weight:500}
@media (prefers-reduced-motion:reduce){.projvid video{display:none}.projvid .vid-still{display:block}}

/* ---------- project grid ---------- */
.pgrid,.grid3{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:36px}
@media(max-width:860px){.pgrid,.grid3{grid-template-columns:repeat(2,1fr)}}
@media(max-width:520px){.pgrid,.grid3{grid-template-columns:1fr}}
.pcard,.proj{border:1px solid var(--rule);background:#fff;text-decoration:none;display:block;border-radius:0;box-shadow:none;transition:.18s;overflow:hidden}
.pcard:hover,.proj:hover{border-color:var(--steel);transform:translateY(-2px)}
.pcard .ph{aspect-ratio:4/3;overflow:hidden;background:var(--paper-2)}
.pcard .ph img,.proj img{width:100%;height:100%;object-fit:cover;display:block}
.pmeta{padding:15px 17px 19px}
.pmeta h3{font-size:18px;margin-bottom:3px}
.pmeta small{font-family:var(--mono);font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--stone)}

/* ---------- filter chips (project gallery) ---------- */
.filters,.gfilters,.chiprow{display:flex;flex-wrap:wrap;gap:8px;margin-top:26px;justify-content:center}
.chip,.gfilter{font-family:var(--mono);font-size:12px;letter-spacing:.09em;text-transform:uppercase;padding:8px 14px;border:1px solid var(--rule);border-radius:0;background:#fff;color:var(--ink-soft);cursor:pointer;transition:.15s}
.chip:hover,.gfilter:hover{border-color:var(--steel);color:var(--navy)}
.chip.on,.gfilter[aria-pressed="true"]{background:var(--navy);color:#fff;border-color:var(--navy)}

/* ---------- photo gallery grid ---------- */
.workgrid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media(min-width:760px){.workgrid{grid-template-columns:repeat(3,1fr)}}
.workfig{position:relative;border:1px solid var(--rule);border-radius:0;overflow:hidden;aspect-ratio:4/3;margin:0;box-shadow:none;background:var(--paper-2)}
.workfig img{width:100%;height:100%;object-fit:cover;transition:transform .5s ease}
.workfig:hover img{transform:scale(1.04)}
.workfig.hide{display:none}
.workfig figcaption{position:absolute;left:0;right:0;bottom:0;background:linear-gradient(0deg,rgba(19,42,68,.85),transparent);color:#fff;font-family:var(--mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;padding:22px 12px 10px}

/* ---------- spec table ---------- */
.spec{width:100%;border-collapse:collapse;margin-top:26px;font-size:15.5px}
.spec td{padding:13px 0;border-bottom:1px solid var(--rule);vertical-align:top}
.spec td:first-child{font-family:var(--mono);font-size:11px;letter-spacing:.09em;text-transform:uppercase;color:var(--stone);width:38%;padding-right:20px}

/* ---------- investment / drivers ---------- */
.range{font-family:var(--display);font-size:clamp(32px,4.6vw,54px);line-height:1;margin:12px 0 6px;color:var(--navy)}
.range-sub{font-family:var(--mono);font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--stone);display:block;margin-top:12px;line-height:1.6}
.drivers{list-style:none;display:grid;gap:12px;margin-top:20px}
.drivers li{font-size:15.5px;color:var(--ink-soft);padding-bottom:12px;border-bottom:1px solid var(--rule);display:flex;justify-content:space-between;gap:16px}
.drivers b{font-family:var(--mono);font-size:11px;letter-spacing:.05em;font-weight:500;white-space:nowrap;color:var(--navy)}

/* ---------- before / after slider ----------
   <img> layers driven by --pos, not the mock's SVGs. The before layer is
   clipped from the right, so dragging reveals it over the after layer. */
.ba-wrap{max-width:960px;margin:0 auto}
.ba{position:relative;aspect-ratio:16/10;border:1px solid var(--rule);border-radius:0;overflow:hidden;background:var(--paper-2);touch-action:pan-y}
.ba img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;-webkit-user-drag:none;user-select:none}
.ba .after-layer{z-index:1}
.ba .before-layer{z-index:2;clip-path:inset(0 calc(100% - var(--pos,50%)) 0 0)}
.ba .lbl{position:absolute;top:12px;z-index:4;font-family:var(--mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;padding:5px 9px;border-radius:0;color:#fff;background:var(--navy)}
.ba .lbl.b{left:12px}
.ba .lbl.a{right:12px;background:var(--steel)}
.ba .divider{position:absolute;top:0;bottom:0;left:var(--pos,50%);width:2px;background:#fff;z-index:3;transform:translateX(-50%);box-shadow:0 0 0 1px var(--navy)}
.ba .knob{position:absolute;top:50%;left:var(--pos,50%);z-index:3;width:44px;height:44px;border-radius:50%;background:#fff;border:1px solid var(--navy);transform:translate(-50%,-50%);display:grid;place-items:center;color:var(--navy);font-family:var(--mono);font-size:13px;pointer-events:none}
.ba input[type=range]{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:ew-resize;z-index:5}
.ba .cap{position:absolute;left:0;right:0;bottom:0;z-index:4;background:linear-gradient(0deg,rgba(19,42,68,.82),transparent);color:#fff;padding:30px 16px 14px;font-family:var(--mono);font-size:11px;letter-spacing:.09em;text-transform:uppercase}
.ba-ui{display:flex;align-items:center;justify-content:center;gap:14px;margin-top:16px}
.ba-ui button{width:42px;height:42px;border-radius:0;border:1px solid var(--rule);background:#fff;color:var(--navy);cursor:pointer;font-size:17px;display:grid;place-items:center;transition:.15s}
.ba-ui button:hover{border-color:var(--steel);color:var(--steel)}
.ba-ui .dots{display:flex;gap:8px;align-items:center}
.ba-ui .dots button{width:9px;height:9px;padding:0;border-radius:50%;border:1px solid var(--stone);background:transparent;cursor:pointer}
.ba-ui .dots button[aria-current="true"]{background:var(--navy);border-color:var(--navy)}
.ba-wrap .hint{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--stone);text-align:center;margin-top:12px}

/* ---------- brand chips ---------- */
.brandrow{display:flex;flex-wrap:wrap;gap:10px 12px;margin-top:26px;justify-content:center}
.brandrow .bw,.brandrow span{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;border:1px solid var(--rule);background:#fff;padding:10px 15px;color:var(--ink-soft)}
.brandrow .bw.hi{border-color:var(--steel);color:var(--navy)}

/* ---------- service area ---------- */
/* The four counties run across the width. minmax(0,1fr) rather than a bare fr:
   an fr track takes its min-width from content, and these lists are long enough
   that one county blew its track out and squeezed the others - the same failure
   the map layout hit here before. */
.tl{list-style:none;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:0 clamp(18px,2.4vw,34px);margin-top:34px;text-align:left}
@media(max-width:900px){.tl{grid-template-columns:repeat(2,minmax(0,1fr))}}
.tl li{padding:15px 0 18px;border-top:1px solid var(--rule);font-size:14px;color:var(--ink-soft)}
.tl b{display:block;margin-top:7px;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.08em;color:var(--steel-ink);font-weight:500;line-height:1.85}

/* ---------- booking calendar ---------- */
.cal{border:1px solid var(--rule);background:#fff;padding:22px}
.cal-head{display:flex;justify-content:space-between;align-items:baseline;gap:14px;padding-bottom:14px;border-bottom:1px solid var(--rule)}
.cal-head strong{font-family:var(--display);font-size:19px;color:var(--navy);font-weight:500}
.cal-head span{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--stone)}
.days{display:grid;grid-template-columns:repeat(5,1fr);gap:8px;margin:18px 0}
@media(max-width:560px){.days{grid-template-columns:repeat(3,1fr)}}
.day{border:1px solid var(--rule);background:#fff;padding:12px 6px;text-align:center;cursor:pointer;transition:.15s}
.day:hover{border-color:var(--steel)}
.day.on{background:var(--navy);border-color:var(--navy);color:#fff}
.day.off{opacity:.35;cursor:not-allowed}
.day .dw{font-family:var(--mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;display:block;color:var(--stone)}
.day.on .dw{color:var(--steel-lt)}
.day .dn{font-family:var(--display);font-size:22px;font-weight:500;color:var(--navy);display:block;line-height:1.3}
.day.on .dn{color:#fff}
.slots{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
@media(max-width:560px){.slots{grid-template-columns:repeat(2,1fr)}}
.slot{border:1px solid var(--rule);background:#fff;padding:11px 4px;text-align:center;font-family:var(--mono);font-size:12px;letter-spacing:.04em;color:var(--navy);cursor:pointer;transition:.15s}
.slot:hover{border-color:var(--steel)}
.slot.on{background:var(--steel);border-color:var(--steel);color:#fff}
.cal-zone{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;color:var(--stone);margin-top:16px;padding-top:14px;border-top:1px dashed var(--rule);line-height:1.7}
.cal-zone b{color:var(--navy);font-weight:500}

/* ---------- seasonal note ---------- */
.season{border-left:3px solid var(--sand);background:#fff;padding:18px 22px;margin-top:30px}
.season b{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--steel);display:block;margin-bottom:6px;font-weight:500}
.season p{font-size:15.5px;color:var(--ink-soft)}

/* ---------- breadcrumb ---------- */
/* 12px: breadcrumbs are navigation, not decoration - nothing on the site
   should ask a reader to squint at a link that tells them where they are. */
.crumb{font-family:var(--mono);font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--stone);padding:20px 0 0}
/* An eyebrow directly under a breadcrumb is the same size, weight, colour and
   case, with nothing between them - so "KITCHEN REMODELING" / "KITCHEN
   RENOVATIONS" reads as one wrapped breadcrumb rather than a crumb and a label.
   Seven pages where the two were identical had the eyebrow removed outright;
   this separates the rest. */
.crumb + .ey{margin-top:14px}
/* --steel is 3.96:1 on paper and 4.15:1 on white - under AA at 12px, on every
   page with a light hero. It was invisible for as long as it existed: the
   contrast checker gives up when it meets an ancestor with a background-image,
   and .shero carries a linear-gradient, so the crumb link was never measured on
   any of those ~30 pages. It only surfaced when /projects got a flat-colour
   hero and the walk finally terminated. --steel-ink clears AA on paper, paper-2
   and white, and stays in the same family. Dark heroes are unaffected:
   .shero-bleed .crumb a overrides with --steel-lt on navy. */
.crumb a{text-decoration:none;color:var(--steel-ink);display:inline-block;padding:5px 3px;margin:-5px -3px}
/* Touch targets. WCAG 2.5.8 asks for 24x24 CSS px; breadcrumb links measured
   14px tall and the footer legal links 21px. Padding grows the hit area and the
   matching negative margin keeps the layout byte-for-byte where it was. */
.fbot a,.legal a{display:inline-block;padding:6px 4px;margin:-6px -4px}

/* ---------- form ---------- */
.qf .wrap{max-width:1180px}
.qf-solo{max-width:680px}
.qcard{border:1px solid var(--rule);background:#fff;padding:clamp(22px,3vw,34px);border-radius:0;box-shadow:none}
.field{display:grid;gap:6px;margin-bottom:16px}
.field label{font-family:var(--mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-soft)}
.field input,.field select,.field textarea{font-family:var(--body);font-size:16px;font-weight:300;padding:12px 13px;border:1px solid var(--rule);background:#fff;color:var(--ink);border-radius:0;width:100%}
.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--steel);box-shadow:inset 0 -2px 0 var(--steel)}
.pair,.qf .grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media(max-width:600px){.pair,.qf .grid{grid-template-columns:1fr}}
.consent{font-size:12px;color:var(--stone);line-height:1.5;margin:4px 0 18px;display:flex;gap:9px}
.consent input{margin-top:3px;width:auto}

/* ---------- FAQ ---------- */
.faqlist details,details{border-bottom:1px solid var(--rule);padding:18px 0}
summary{font-family:var(--display);font-size:19px;color:var(--navy);cursor:pointer;list-style:none;display:flex;justify-content:space-between;gap:18px;align-items:baseline;font-weight:500}
summary::-webkit-details-marker{display:none}
summary .pm{font-family:var(--mono);color:var(--steel);font-weight:400}
details[open] summary .pm{transform:none}
details .ans,details p{margin-top:10px;color:var(--ink-soft);font-size:15.5px;max-width:68ch}

/* ---------- footer ---------- */
footer{background:var(--tile);color:#93A0AD;font-size:14px;padding:48px 0 30px}
footer .wrap{display:flex;flex-wrap:wrap;gap:26px;justify-content:space-between}
footer h3{font-family:var(--mono);font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--sand);margin-bottom:10px;font-weight:400}
footer a{text-decoration:none;display:block;padding:3px 0}
footer a:hover{color:#fff}
footer img{border-radius:0}
.fcols{display:grid;grid-template-columns:1fr;gap:26px;width:100%}
/* three columns, because .fcols has three children - the brand block, Services
   and Contact. The fourth track was 223px of dead space on the right of every
   page. */
@media(min-width:920px){.fcols{grid-template-columns:1.4fr 1fr 1fr}}
.fsocial{display:flex;gap:12px;margin-top:14px}
.fsocial a{display:grid;place-items:center;width:36px;height:36px;border:1px solid #262C33;padding:0}
.fsocial a svg{width:17px;height:17px}
.fsocial a:hover{border-color:var(--sand)}
.fbbb{display:inline-block;margin-top:14px;font-family:var(--mono);font-size:12px;letter-spacing:.1em;text-transform:uppercase;border:1px solid #262C33;padding:8px 12px}
.fbbb b{color:var(--sand);font-weight:400}
.fbbb:hover{border-color:var(--sand)}
.fbot,.legal{font-family:var(--mono);font-size:12px;letter-spacing:.08em;color:#75818D;margin-top:26px;width:100%;border-top:1px solid #262C33;padding-top:18px}

/* ---------- sticky mobile call bar ---------- */
.callbar{position:fixed;left:0;right:0;bottom:0;z-index:95;display:grid;grid-template-columns:1fr 1fr;border-top:1px solid var(--rule);background:var(--paper)}
.callbar a{padding:15px 8px;text-align:center;text-decoration:none;font-family:var(--mono);font-size:12px;letter-spacing:.1em;text-transform:uppercase}
.callbar .c{color:var(--navy);border-right:1px solid var(--rule)}
.callbar .q{background:var(--navy);color:#fff}
@media(min-width:1000px){.callbar{display:none}}
@media(max-width:999px){body{padding-bottom:52px}}

/* ---------- misc layout ---------- */
.essentials{display:grid;gap:16px}
@media(min-width:760px){.essentials{grid-template-columns:1fr 1fr}.essentials > :last-child:nth-child(odd){grid-column:1 / -1}}
.hero-meta{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:24px;font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--stone)}
.dark .hero-meta{color:var(--steel-lt)}
@media(prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}


/* The nav phone number overflowed at 390: it is nowrap, and the row also holds
   the logo and the burger. The sticky call bar already offers "Call now" on
   exactly these widths, so hiding it here loses nothing. */
@media(max-width:640px){.navcall{display:none}}

/* ---- inline SVG sizing --------------------------------------------------
   An <svg> with a viewBox and no width/height attributes has no intrinsic
   size and expands to fill its container. That rendered the trust-bar checks
   at 1060x1060 and is the same defect that broke the map.

   :where() contributes zero specificity, so this whole selector weighs the
   same as a bare `svg` and ANY real rule beats it. It is a floor, not a
   constraint - and it covers icons added later without anyone remembering. */
svg:where(:not([width]):not([height])){width:1.15em;height:1.15em;flex:none}

/* the two icon contexts that were blowing up, sized deliberately */
.trustrow .ti svg,.srlist li svg{width:18px;height:18px;flex:none;color:var(--steel)}
.trustrow .ti,.srlist li{display:flex;align-items:center;gap:9px}
.srlist{list-style:none;display:grid;gap:10px;margin-top:16px}
.srlist li{font-size:15.5px;color:var(--ink-soft)}

/* ---- photo reel ------------------------------------------------------------
   The track is width:max-content, so the base img{max-width:100%} resolves
   against a track that is already enormous and constrains nothing. These tiles
   must be sized by HEIGHT. Without this rule the section measured 70,158px once
   its lazy images loaded - and it measured a harmless 1,171px before they did,
   which is why a geometry audit has to force loading=eager first. */
.reel{position:relative;background:var(--paper-2);overflow:hidden;padding:0}
.reel-mask{overflow:hidden;-webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent)}
.reel-track{display:flex;gap:18px;width:max-content;padding:22px 0;animation:reelscroll 175s linear infinite;will-change:transform}
.reel:hover .reel-track{animation-play-state:paused}
.reel-tile{flex:0 0 auto;margin:0;border:1px solid var(--rule);border-radius:0;overflow:hidden;box-shadow:none}
.reel-tile img{height:clamp(190px,24vw,300px);width:auto;max-width:none;display:block;object-fit:cover}
@media(prefers-reduced-motion:reduce){.reel-track{animation:none}.reel-mask{overflow-x:auto}}
@keyframes reelscroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* ============================================================================
   RECOVERED COMPONENT CSS

   These 62 rules covered 30 classes across 8 sections and went out with the old
   inline block when the design system landed. The markup still used them, so
   the sections rendered with no layout at all: "who" stacked flush, "how" broke
   its numbered steps apart, "reviews" lost its cards entirely.

   Found by diffing three sets rather than by eye - classes USED in the markup,
   classes STYLED now, and classes that WERE styled in the old block. The
   interesting set is used AND previously styled AND no longer styled. That
   audit is scratchpad/css-audit.js and is worth re-running after any bulk CSS
   change.

   Layout is verbatim from the original. Only the surface was retranslated.
   ========================================================================= */
.hero-rating.light{color:#fff}
.hero-rating.light .rcount{color:rgba(255,255,255,.85)}
.top{padding:clamp(28px,5vw,52px) 0 clamp(24px,4vw,40px);background:linear-gradient(180deg,var(--sky-2),#fff)}
.top .head{text-align:center;max-width:60ch;margin:0 auto clamp(22px,3vw,34px)}
.top h1,.top h2{font-size:var(--fs-hero);margin:10px 0 12px}
.top .sub{color:var(--muted);font-size:clamp(1rem,.95rem + .3vw,1.15rem);max-width:50ch;margin:0 auto}
.gbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:10px 18px;text-align:center;margin-bottom:30px}
.gbar .g{font-family:var(--mono);font-weight:700;font-size:20px;color:var(--navy);display:inline-flex;align-items:center;gap:8px}
.gbar .stars{color:var(--sand);font-size:22px;letter-spacing:2px}
.gbar .score{font-family:var(--mono);font-weight:700;font-size:22px;color:var(--navy)}
.gbar .pill{background:var(--paper-2);color:var(--navy);font-weight:600;font-size:13px;padding:7px 14px;border-radius:0}
.rhi{display:grid;grid-template-columns:1fr;gap:16px}
.rcard{background:var(--sky-2);border:1px solid var(--line);border-radius:var(--r);padding:22px;display:flex;flex-direction:column}
.rcard .stars{color:var(--sand);font-size:16px;letter-spacing:1px;margin-bottom:10px}
.rcard p{font-size:15.5px;color:var(--ink);margin-bottom:12px;display:-webkit-box;-webkit-line-clamp:6;line-clamp:6;-webkit-box-orient:vertical;overflow:hidden}
.rcard .who{display:flex;align-items:center;gap:9px;font-size:13px;color:var(--muted);font-weight:600;margin-top:auto}
.rcard .av{width:30px;height:30px;border-radius:50%;background:var(--blue);color:#fff;display:grid;place-items:center;font-family:var(--mono);font-weight:700;font-size:13px}
.who-badge{display:inline-flex;align-items:center;gap:8px;background:var(--paper-2);color:var(--navy);font-family:var(--mono);font-weight:600;font-size:13px;padding:7px 14px;border-radius:0;margin-bottom:12px}
.who-vals{display:grid;grid-template-columns:1fr;gap:16px}
.vcard{background:var(--sky-2);border:1px solid var(--line);border-top:4px solid var(--blue);border-radius:var(--r);padding:26px 22px;text-align:center}
.vcard .vic{width:48px;height:48px;border-radius:50%;background:var(--paper-2);display:grid;place-items:center;margin:0 auto 12px;font-size:21px}
.vcard h3{font-size:18px;margin-bottom:6px}
.vcard p{color:var(--muted);font-size:14.5px}
@media(min-width:680px){
  .who-vals{grid-template-columns:repeat(3,1fr)}
}
.faq h2{font-size:var(--fs-h2);margin-bottom:6px}
.faq .note{color:var(--muted);font-size:15px;margin-bottom:18px}
@media(min-width:620px){
  .rhi{grid-template-columns:repeat(3,1fr)}
}
.gbar a.g,.gbar a.pill{cursor:pointer;text-decoration:none;transition:.15s}
.gbar a.g:hover{text-decoration:underline}
.gbar a.pill:hover{filter:brightness(.96)}
.reviews-cta{text-align:center;margin-top:24px}
.brands{padding:clamp(30px,4vw,50px) 0;background:var(--sky-2);border-top:1px solid var(--line);border-bottom:1px solid var(--line);text-align:center}
.brands h2{font-size:var(--fs-h2);margin:8px 0 8px}
.brands .bsub{color:var(--muted);max-width:54ch;margin:0 auto 24px}
.fhours{color:inherit}
@media(min-width:900px){
  .who-split{display:grid;grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);gap:clamp(28px,4vw,52px);align-items:center}
}
.who-photo img{width:100%;border-radius:var(--r);box-shadow:none;aspect-ratio:4/5;object-fit:cover}
@media(max-width:899px){
  .who-photo{margin-bottom:24px}
  .who-photo img{aspect-ratio:16/10}
}
.who-text{text-align:left}
.who-text .who-badge{display:inline-block;margin-bottom:10px}
.who-text>p{color:var(--muted);margin:8px 0 0;max-width:54ch}
.who .who-vals{margin-top:clamp(26px,3vw,38px)}
.howsteps{display:grid;grid-template-columns:1fr;gap:20px;margin-top:28px;text-align:left}
@media(min-width:560px){
  .howsteps{grid-template-columns:1fr 1fr}
}
@media(min-width:980px){
  .howsteps{grid-template-columns:repeat(4,1fr)}
}
.howstep{background:#fff;border:1px solid var(--line);border-radius:var(--r);padding:24px 22px}
.howstep .hnum{display:inline-grid;place-items:center;width:38px;height:38px;border-radius:50%;background:var(--navy);color:#fff;font-family:var(--mono);font-weight:700;margin-bottom:12px}
.howstep h3{font-size:18px;margin-bottom:6px}
.howstep p{color:var(--muted);font-size:14px}
.ctaband{background:var(--navy);color:#fff;text-align:center;padding:clamp(44px,6vw,76px) 0}
.ctaband h2{color:#fff;font-size:var(--fs-h2)}
.ctaband p{color:#cfe0f0;margin:10px 0 22px}
.ctaband .cta-row{display:flex;gap:12px;justify-content:center;flex-wrap:wrap}
.btn-ghost-d{background:transparent;color:#fff;border-color:rgba(255,255,255,.5)}
.btn-ghost-d:hover{background:rgba(255,255,255,.12);border-color:#fff;color:#fff}
.showroom .sr-img{position:relative;border-radius:var(--r);overflow:hidden;aspect-ratio:4/3;box-shadow:none}
.showroom .sr-img img{width:100%;height:100%;object-fit:cover}
.showroom .sr-tag{position:absolute;left:14px;top:14px;background:var(--blue);color:#fff;font-family:var(--mono);font-weight:700;font-size:12px;letter-spacing:.04em;padding:7px 12px;border-radius:0;text-transform:uppercase}
.showroom .sp{color:var(--muted);margin-bottom:18px;max-width:54ch}
.hero-rating .rcount{color:var(--muted);font-weight:500}
.hero-rating:hover .rcount{text-decoration:underline}

/* The "Family-owned" photo was edge-cropped with its caption floating loose.
   Framed like every other piece of media on the page: hairline border, paper
   ground, square corners, and the badge sitting on the frame rather than
   drifting above it. */
.who-photo{border:1px solid var(--rule);background:var(--paper-2);overflow:hidden;display:block}
.who-photo img{display:block;margin:0}
.who-text .who-badge{background:var(--paper-2);border:1px solid var(--rule);color:var(--navy);font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;font-weight:500;padding:7px 12px;border-radius:0}

/* ---- recovered from the previous shared stylesheet -------------------------
   Eight selectors were still used in markup but had no rule after site.css was
   rewritten: .shero and .shero__img (every interior page hero), .fcard,
   .sidecard, .babox, .trust-mini, .two and .in. Same failure as the page-level
   recovery, one level up - the SHARED sheet lost them too. Layout verbatim,
   surface retranslated. */
.shero{background:linear-gradient(180deg,var(--sky-2),#fff);padding:clamp(28px,5vw,52px) 0 clamp(30px,5vw,56px)}

/* A full-bleed hero is dark. It says so at .shero-bleed 480 lines up - and that
   declaration has never once applied, because THIS rule is later in the file
   and both are a single class, so source order decides and .shero wins. Every
   bleed hero has been carrying a transparent background-color and the light
   gradient above, invisible only because .shero-media paints a photograph over
   the whole thing.

   That is fine until the photograph does not arrive. The copy in these heroes is
   white; the ground behind it, once the image layer is gone, is a gradient
   running to #fff. Not ugly - unreadable. The image fallback added earlier hides
   a failed <img>, which makes the failure MORE likely to expose this, not less.

   Doubling the class takes it to (0,2,0) so it wins on specificity rather than
   on position, and cannot be undone by someone moving a block.

   Deliberately background ONLY. The same collision also hands .shero-bleed the
   wrong padding - 52px/56px instead of 0/34-58 - but the interior layout has
   grown up around that for 8 pages, and correcting it here would move content
   on all of them for no stated reason. Reported, not bundled. */
.shero.shero-bleed{background:var(--navy-deep)}
.shero__img{border-radius:var(--r);overflow:hidden;aspect-ratio:4/3;box-shadow:none}
/* the interior hero's own type. Was page-inline on all 40 pages that had a
   hero; shared now because the five pages restored from 2799df1^ carry no
   inline block of their own. Same value, so nothing else changes. */
.shero h1{font-size:var(--fs-hero);margin:6px 0 12px}
.shero .upd{max-width:64ch}
.shero__img img{width:100%;height:100%;object-fit:cover}
.sidecard{background:var(--sky-2);border:1px solid var(--line);border-top:4px solid var(--blue);border-radius:var(--r);padding:24px}
.sidecard h3{font-size:18px;color:var(--navy);margin-bottom:10px}
/* text-align:left on the CARD, not on the section that happens to hold it. A
   card in a .wrap.center inherited centred body copy - which is how the new
   "Two ways to start" pair came out centred while the same component reads left
   on 32 other pages. A card should look like a card wherever it is put. */
.fcard{background:var(--sky-2);border:1px solid var(--line);border-radius:var(--r);padding:22px;text-align:left}
.fcard h3{font-size:17px;color:var(--navy);margin-bottom:5px}
.fcard p{color:var(--muted);font-size:14px}
.babox{background:var(--sky-2);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.band .in{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:20px;padding:clamp(40px,5vw,60px) 0}
/* The closing CTA band, on 39 pages. Its two buttons carry inline white text
   - background:#fff;color:var(--navy) on one, color:#fff on the other - which
   only makes sense on a dark band. site.css gave .band padding and nothing
   else, so the Call button was white text on the near-white page background:
   invisible, measured at 1.08:1. This is the same component the homepage uses
   as .ctaband. :not([style]) keeps the two inline background:var(--sky-2)
   bands on /about light, which is what they are for. */
section.band:not([style]){background:var(--navy);color:#fff}
section.band:not([style]) h2{color:#fff}
section.band:not([style]) p{color:#cfe0f0}
.two{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.trust-mini{display:flex;flex-wrap:wrap;gap:8px 16px;margin-top:14px;font-size:13px;color:var(--muted)}
.trust-mini span{display:inline-flex;align-items:center;gap:6px}

/* ---- interior page hero -----------------------------------------------------
   .wrap.grid is the interior hero: copy on the left, photo on the right. It is
   used on 40 pages and had no rule after the stylesheet was rewritten, so every
   one of those heroes stacked and the photo ran full width - which is what made
   those sections measure ~1100-1300px.

   minmax(0,...) not bare fr, the same hazard that collapsed the map column. */
.wrap.grid{display:grid;gap:clamp(24px,4vw,52px);align-items:center}
@media(min-width:900px){.wrap.grid{grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr)}}
.shero__img,.story__img{border:1px solid var(--rule);overflow:hidden;background:var(--paper-2);border-radius:0}
.shero__img img,.story__img img{display:block;width:100%;height:100%;object-fit:cover}

/* ---------- an image whose source could not be loaded ---------- */
/* A broken <img> keeps its box and paints the alt text inside it, so a remote
   host going down turns a photo into a grey rectangle full of prose. The
   figure goes with the image; see the head partial for why it hides rather
   than substituting a stand-in photo. */
/* !important because a more specific rule wins otherwise: .pcard-img img
   is (0,1,1) and this selector is (0,1,0), so the card images stayed visible
   as empty frames while every figure on the page hid correctly. A fallback
   that can be out-specified is a fallback that works only where nothing else
   styles the image. */
[data-img-failed]{display:none !important}


/* /about - "Who you'll be working with".

   In site.css rather than page-inline on purpose. The /about page already
   defines .vals, .scard and .story__img inside its own <style> block, and the
   last component that lived only in a page (.mini) rendered as three undivided
   spans the day it was used somewhere else.

   .person__ph is an image slot with no image in it: the ratio, crop, radius and
   border are set here, so dropping an <img> inside the div later needs no CSS
   and shifts no layout. 16/10 matches .tile-img rather than .story__img: at 4/3 a
   572px column made a 429px slot, and two of them put the section at 923px -
   over the harness tall-section flag, and a lot of empty ground while the
   photos are pending. */
/* align-items:start, not the grid default of stretch: the two bios are
   different lengths and a stretched track makes the shorter block claim
   height it is not using. Level tops read as a pair. */
.people__grid{display:grid;grid-template-columns:1fr;gap:30px;margin-top:30px;
  text-align:left;align-items:start}
@media(min-width:760px){.people__grid{grid-template-columns:repeat(2,1fr);gap:36px}}
/* CURRENTLY UNUSED - no element in site/ carries .person__ph. Kept on purpose:
   the photo slots were removed from /about on 2026-08-26 because two empty
   frames outweighed the bios, and this rule is what lets a photo go back in
   as one line of markup instead of a rebuilt component. /about carries the
   markup to restore in a comment beside each name. Do not delete as dead
   code; it is dormant, not orphaned. */
.person__ph{aspect-ratio:16/10;border-radius:var(--r);overflow:hidden;background:var(--sky-2);
  border:1px solid var(--line);display:grid;place-items:center;margin-bottom:18px}
.person__ph img{width:100%;height:100%;object-fit:cover;display:block}
.person__mono{font-family:var(--mono);font-weight:700;font-size:clamp(30px,4.4vw,44px);
  letter-spacing:.06em;color:var(--blue);opacity:.55}
.person h3{margin:0 0 3px;font-size:clamp(20px,2.1vw,24px)}
.person .role{font-family:var(--mono);font-size:12.5px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--muted);font-weight:700;margin-bottom:12px}
.person p{margin:0}
/* the trust-line treatment, centred under the pair rather than left-aligned
   under one of them */
.people__note{justify-content:center;text-align:center;margin-top:30px}
