/* ==========================================================================
   Ozzifloors — main site design system: "contractor clean".

   Re-skin of the existing class inventory onto the same system that ships on
   the Google Ads landing pages (assets/css/lp.css): warm neutrals, one gold
   accent, self-hosted Poppins for everything, clear alternating section
   bands, buttons that look like buttons, cards with real structure.

   Nothing here renames or removes a class. Every class the Python builders
   emit is still styled. No serif, no editorial italics, no CDN, no external
   request of any kind.
   ========================================================================== */

/* --- 1. Fonts ------------------------------------------------------------ */
/* Four static latin-subset faces, ~8KB each, served from assets/fonts/.
   font-display:swap, so the geometric fallback stack paints instantly. */

@font-face{
  font-family:"Poppins";
  src:url("../fonts/poppins-latin-400.woff2") format("woff2");
  font-weight:400;font-style:normal;font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
                U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,
                U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Poppins";
  src:url("../fonts/poppins-latin-500.woff2") format("woff2");
  font-weight:500;font-style:normal;font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
                U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,
                U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Poppins";
  src:url("../fonts/poppins-latin-600.woff2") format("woff2");
  font-weight:600;font-style:normal;font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
                U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,
                U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Poppins";
  src:url("../fonts/poppins-latin-700.woff2") format("woff2");
  font-weight:700;font-style:normal;font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
                U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,
                U+2212,U+2215,U+FEFF,U+FFFD;
}

/* --- 2. Tokens ----------------------------------------------------------- */
/* THE CONTRACT. These names are shared with the page builders — do not
   rename them. Contrast figures are measured, not estimated. */

:root{
  /* Warm surfaces */
  --paper:        #FFFFFF;
  --paper-2:      #FAF6F0;   /* off-white band */
  --sand:         #F3EDE3;   /* warmer band */
  --tint:         #FCF4E3;   /* pale gold tint */
  --dark:         #221C17;   /* warm near-black band */
  --dark-2:       #2E2620;
  --plate:        #16161F;   /* matches the logo file's own background */
  /* The chrome ground. Header, dropdown panels, drawer and footer all sit on
     --plate so the supplied dark-background logo lockup blends into the bar
     instead of reading as a pasted-on rectangle. --plate-2 is the one-step
     lift used for panels that float above the bar; --plate-line is the only
     rule colour allowed on that ground (a neutral tint of --on-dark, so it
     never goes warm against the cool near-black). */
  --plate-2:      #1E1E29;
  --plate-line:   rgba(247,243,237,.13);
  --plate-line-2: rgba(247,243,237,.26);

  /* Warm ink */
  --ink:          #1A1512;   /* 18.1:1 on white */
  --body:         #3D3833;   /* 11.6:1 on white */
  --muted:        #6B635B;   /*  5.9:1 on white, 5.0:1 on sand */
  --line:         #E8E1D6;
  --line-2:       #D6CCBE;

  /* On the dark band */
  --on-dark:      #F7F3ED;   /* 15.2:1 on --dark */
  --on-dark-mut:  #C3B8A9;   /*  8.6:1 on --dark */
  --line-dark:    #3D342B;

  /* The single accent.
     --gold      brand value. Fills, rules, icons, graphic elements, large
                 type, and text on the dark band only (6.9:1). Never small
                 text on light.
     --gold-hover  pressed / hover fill.
     --gold-ink  darker gold for text on light surfaces. 5.48:1 on white,
                 5.0:1 on tint, 4.66:1 on sand. AA at every size.
     --gold-soft pale border tint for chips and inactive graphic states. */
  --gold:         #D49D11;
  --gold-hover:   #B07F0A;
  --gold-ink:     #8A6206;
  --gold-soft:    #F0D89A;

  /* Feedback */
  --err:          #B3261E;
  --err-bg:       #FDF3F2;

  /* Type. One family, matched to the logo wordmark. --display is an alias of
     --sans on purpose: the serif tier is gone. */
  --sans: "Poppins","Century Gothic","Avenir Next",Avenir,"Segoe UI",Roboto,
          system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif;
  --display: var(--sans);

  /* Metrics. One fluid gutter, no stepped ladder: linear in the viewport,
     floors at 20px, passes through 32px at 700px, caps at 76px at 1240px
     where --maxw takes over. Left and right padding are one declaration, so
     symmetry cannot drift. */
  --gutter: clamp(20px, calc(8.15vw - 25px), 76px);
  --maxw: 1264px;
  --header-h: 68px;
  /* Height of the wordmark ARTWORK, not of a plate. logo-mark.webp is trimmed
     flush to the ink, so this number is the ink height directly — the old
     lockup wasted about 14% of its box on plate margin and needed a larger
     value to render the same-sized wordmark. */
  --logo-h: 42px;

  --pad-section: clamp(3.5rem, 7vw, 6.6rem);
  --radius: 8px;
  --radius-lg: 14px;
  --radius-img: 10px;

  --shadow: 0 1px 2px rgba(34,28,23,.04), 0 8px 22px rgba(34,28,23,.06);
  --shadow-lg: 0 3px 8px rgba(34,28,23,.07), 0 20px 48px rgba(34,28,23,.14);
  --ease: cubic-bezier(.22,.61,.36,1);

  /* ---- Legacy aliases -------------------------------------------------
     The generated pages still carry inline var() references from the old
     palette. Every one of them is mapped onto the new system here so that
     nothing anywhere resolves to an empty string. Do not delete these
     until the builders are clean. */
  --white:        var(--paper);
  --rule:         var(--line);
  --rule-dark:    var(--line-dark);
  --ink-2:        var(--dark);
  --ink-3:        var(--dark-2);
  --body-light:   var(--muted);
  --gold-deep:    var(--gold-ink);
  --gold-light:   var(--gold);
  --on-dark-dim:  var(--on-dark-mut);
  --container:    var(--maxw);
}

@media (min-width:900px){ :root{ --header-h:82px; --logo-h:52px; } }

/* --- 3. Reset ------------------------------------------------------------ */

*,*::before,*::after{box-sizing:border-box}
/* Grid and flex children default to min-width:auto, so a long word, a wide
   table or a padded card can push a track past the viewport. That used to be
   masked by overflow-x:hidden on <html>, which was also silently killing
   every position:sticky descendant (the header was declared sticky and still
   scrolled away). With the clip removed from <html>, the tracks have to be
   genuinely shrinkable instead. */
.wrap > *, [class*="grid"] > *, .g2 > *, .g3 > *, .g4 > *{min-width:0}

/* overflow-x:hidden must NOT go on <html>. It makes the scroll container the
   element itself rather than the viewport, which silently kills every
   position:sticky descendant: the header was declared sticky and still
   scrolled away, because of this one line. Keeping the clip on <body> alone
   contains horizontal overflow just as well and leaves sticky working. */
html{
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  scroll-padding-top:calc(var(--header-h) + 14px);
}

body{
  margin:0;
  overflow-x:hidden;
  background:var(--paper);
  color:var(--body);
  font-family:var(--sans);
  font-size:16px;
  font-weight:400;
  line-height:1.7;
  letter-spacing:-.004em;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}
@media (min-width:900px){ body{font-size:17px} }

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

h1,h2,h3,h4{
  font-family:var(--display);
  font-weight:700;
  color:var(--ink);
  letter-spacing:-.028em;
  line-height:1.16;
  margin:0;
  text-wrap:balance;
}
h1{font-size:clamp(1.85rem,5.4vw,3rem)}
h2{font-size:clamp(1.5rem,4vw,2.3rem)}
h3{font-size:clamp(1.0625rem,2.2vw,1.3rem);line-height:1.28;letter-spacing:-.02em}
h4{font-size:1rem}

p{margin:0 0 1.05em}
p:last-child{margin-bottom:0}
ul,ol{margin:0;padding:0;list-style:none}

a{color:var(--gold-ink)}
a:hover{color:var(--ink)}
b,strong{font-weight:600}
em{font-style:normal}

button{font:inherit;cursor:pointer;border:0;background:none;color:inherit}
table{border-collapse:collapse}

:focus-visible{outline:3px solid var(--gold-hover);outline-offset:2px;border-radius:3px}
.band-ink :focus-visible,.hero :focus-visible,.ctaband :focus-visible,
.ftr :focus-visible,.drawer :focus-visible,
.hdr :focus-visible,.nav__panel :focus-visible{outline-color:var(--gold)}

/* --- 4. Layout and bands ------------------------------------------------- */

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

.section{padding-block:var(--pad-section);background:var(--paper)}
.section--tight{padding-block:clamp(2.5rem,4.5vw,4rem)}

.band-white {background:var(--paper)}
.band-paper {background:var(--paper-2)}
.band-paper2{background:var(--sand)}
.band-ink   {background:var(--dark);color:var(--on-dark)}
.band-ink h1,.band-ink h2,.band-ink h3,.band-ink h4{color:var(--on-dark)}
.band-ink p{color:var(--on-dark)}
.band-ink a{color:var(--gold)}
.band-ink a:hover{color:var(--on-dark)}

/* --- 5. Typography ------------------------------------------------------- */

.display{
  font-family:var(--display);
  font-weight:700;
  line-height:1.14;
  letter-spacing:-.03em;
  color:var(--ink);
  text-wrap:balance;
}
.band-ink .display,.hero .display,.ctaband .display{color:var(--on-dark)}

.d1{font-size:clamp(2rem,5.4vw,3.25rem)}
.d2{font-size:clamp(1.6rem,4.2vw,2.45rem);line-height:1.14}
.d3{font-size:clamp(1.15rem,2.2vw,1.5rem);line-height:1.22}

/* The old signature was one italic serif word inside a headline. Same hook,
   new system: the accent word is gold and upright. Gold at display size
   clears AA on every band we use it on. */
.display em,.hero__title em,.overlap__title em{font-style:normal;color:var(--gold-ink)}
.band-ink .display em,.hero__title em,.ctaband .display em,
.band-ink .overlap__title em{color:var(--gold)}

.eyebrow{
  display:flex;align-items:center;gap:10px;
  font-family:var(--sans);
  font-size:.6875rem;font-weight:700;
  letter-spacing:.15em;text-transform:uppercase;
  line-height:1.4;
  color:var(--gold-ink);
  margin:0 0 15px;
}
.eyebrow::before{content:"";width:24px;height:4px;border-radius:2px;background:var(--gold);flex:none}
.band-ink .eyebrow,.hero .eyebrow,.ctaband .eyebrow{color:var(--gold)}

.lede{font-size:1.0625rem;line-height:1.6;color:var(--body);text-wrap:pretty}
@media (min-width:900px){ .lede{font-size:1.15rem} }
.band-ink .lede,.hero .lede,.ctaband .lede{color:var(--on-dark)}

.small{font-size:.9375rem;line-height:1.6}
.micro{
  font-size:.6875rem;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;
  color:var(--gold-ink);
}
.band-ink .micro{color:var(--gold)}

/* section heads --------------------------------------------------------- */

.sec-head{
  text-align:center;
  max-width:58ch;
  margin:0 auto clamp(2rem,3.6vw,3.25rem);
  display:flex;flex-direction:column;align-items:center;
}
.sec-head .eyebrow{justify-content:center}
.sec-head__deck{margin-top:14px;color:var(--body);max-width:58ch;text-wrap:pretty}
.band-ink .sec-head__deck{color:var(--on-dark)}
.sec-head__rule{width:64px;height:4px;border-radius:2px;background:var(--gold);margin-top:22px}

/* the asymmetric variant, kept for the few places it is right */
.sec-head--split{
  display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);
  gap:clamp(1.5rem,5vw,4rem);align-items:end;text-align:left;max-width:none;
  padding-bottom:1.5rem;border-bottom:1px solid var(--line);
}
.sec-head--split .eyebrow{justify-content:flex-start}
.band-ink .sec-head--split{border-bottom-color:var(--line-dark)}
@media (max-width:820px){
  .sec-head--split{grid-template-columns:1fr;gap:1.15rem;align-items:start}
}

/* centred head with a gold rule under it */
.chead{
  text-align:center;max-width:56ch;
  margin:0 auto clamp(2rem,3.6vw,3.25rem);
  display:flex;flex-direction:column;align-items:center;
}
.chead .eyebrow{justify-content:center}
.chead__rule{width:64px;height:4px;border-radius:2px;background:var(--gold);margin:20px auto 0}

/* --- 6. Buttons ---------------------------------------------------------- */

.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  min-height:50px;
  padding:13px 22px;
  font-family:var(--sans);
  font-size:1rem;font-weight:600;letter-spacing:-.01em;line-height:1.2;
  text-align:center;text-decoration:none;
  border:2px solid transparent;
  border-radius:var(--radius);
  cursor:pointer;white-space:nowrap;
  max-width:100%;   /* a nowrap label may never widen its column past the page */
  transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .1s ease;
}
.btn:active{transform:translateY(1px)}

.btn__chip{
  width:24px;height:24px;border-radius:50%;
  display:grid;place-items:center;flex:none;
  transition:transform .25s var(--ease),background-color .15s ease;
}
.btn__chip svg{width:13px;height:13px}
.btn:hover .btn__chip{transform:translateX(3px)}

/* Gold fill, warm-black label: 7.5:1. White on gold would be 2.4:1 and fail. */
.btn--gold{background:var(--gold);border-color:var(--gold);color:var(--ink);box-shadow:0 2px 0 rgba(122,88,8,.5)}
.btn--gold .btn__chip{background:rgba(26,21,18,.16)}
.btn--gold:hover{background:var(--gold-hover);border-color:var(--gold-hover);color:var(--ink)}
.btn--gold:hover .btn__chip{background:rgba(26,21,18,.22)}

.btn--ink{background:var(--ink);border-color:var(--ink);color:#fff}
.btn--ink .btn__chip{background:rgba(255,255,255,.18)}
.btn--ink:hover{background:var(--dark-2);border-color:var(--dark-2)}

.btn--ghost{background:transparent;color:var(--ink);border-color:var(--line-2)}
.btn--ghost .btn__chip{background:rgba(26,21,18,.08)}
.btn--ghost:hover{background:var(--ink);border-color:var(--ink);color:#fff}
.btn--ghost:hover .btn__chip{background:rgba(255,255,255,.18)}
.band-ink .btn--ghost{color:var(--on-dark);border-color:rgba(247,243,237,.5)}
.band-ink .btn--ghost:hover{background:var(--on-dark);border-color:var(--on-dark);color:var(--dark)}

.btn--ghost-light{background:transparent;color:#fff;border-color:rgba(255,255,255,.5)}
.btn--ghost-light .btn__chip{background:rgba(255,255,255,.16)}
.btn--ghost-light:hover{background:#fff;border-color:#fff;color:var(--ink)}
.btn--ghost-light:hover .btn__chip{background:rgba(26,21,18,.14)}

.btn-row{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.btn-row--center{justify-content:center}
@media (max-width:399px){
  .btn-row{flex-direction:column;align-items:stretch}
  .btn-row .btn{width:100%}
}

/* text link with a gold rule under it */
.tlink{
  display:inline-flex;align-items:center;gap:9px;
  font-size:1rem;font-weight:600;
  color:var(--gold-ink);text-decoration:none;
  padding-bottom:3px;
  border-bottom:2px solid var(--gold);
  transition:color .15s ease,border-color .15s ease;
}
/* An email address is one unbreakable token; without these it hangs off the
   right edge of a narrow column. */
.tlink{max-width:100%;flex-wrap:wrap;overflow-wrap:anywhere}
.tlink svg{width:16px;height:16px;flex:none;transition:transform .18s ease}
.tlink:hover{color:var(--ink);border-bottom-color:var(--ink)}
.tlink:hover svg{transform:translateX(5px)}
.band-ink .tlink,.ctaband .tlink{color:var(--gold);border-bottom-color:var(--gold)}
.band-ink .tlink:hover,.ctaband .tlink:hover{color:var(--on-dark);border-bottom-color:var(--on-dark)}

/* --- 7. Header ----------------------------------------------------------- */

/* The bar is --plate, the same near-black the supplied logo artwork is baked
   on, so the lockup sits flush in the bar with no plate and no radius of its
   own. Everything in the bar is therefore an on-dark treatment: link colours
   from --on-dark / --on-dark-mut, rules from --plate-line, gold as the accent
   (7.3:1 on --plate). The footer runs on the same ground, so the two ends of
   every page are a matched pair. */
.hdr{
  position:sticky;top:0;z-index:60;
  background:var(--plate);
  border-bottom:1px solid var(--plate-line);
  transition:box-shadow .25s var(--ease);
}
/* Re-tuned for the dark bar: a warm translucent drop shadow is invisible
   against near-black, so the lift is a light hairline on top of a deeper,
   tighter black shadow. */
.hdr.is-stuck{
  border-bottom-color:var(--plate-line-2);
  box-shadow:0 1px 0 rgba(247,243,237,.07),0 16px 32px -22px rgba(0,0,0,.9);
}
.hdr__in{
  display:flex;align-items:center;gap:clamp(1rem,2.4vw,2rem);
  min-height:var(--header-h);
}

/* logo-mark.webp is the wordmark on real transparency, trimmed to the ink.
   It is artwork sitting on the dark bar, not a box on it: no background, no
   radius, no border, no glow. It must only ever be placed on --plate or
   --dark; the wordmark is white and gold and disappears on light grounds. */
.brand{display:flex;align-items:center;gap:.6rem;text-decoration:none;flex:none}
.brand__logo{
  display:block;
  height:var(--logo-h);
  width:auto;
}
/* Kept so the old letter-O lockup still renders if a builder emits it. */
.brand__mark{
  width:34px;height:34px;border-radius:6px;background:var(--plate);
  display:grid;place-items:center;flex:none;
}
.brand__mark span{font-family:var(--display);font-weight:700;font-size:1.15rem;color:var(--gold);line-height:1}
.brand__name{font-family:var(--display);font-weight:700;font-size:1.2rem;letter-spacing:-.03em;color:var(--on-dark);line-height:1}
.brand__name b{font-weight:700}
.ftr .brand__name{color:var(--on-dark)}

/* Categorised dropdown menu.
   The panels open on :hover AND on :focus-within in CSS alone, so every link
   is reachable by mouse, by keyboard and with JavaScript switched off.
   site.js only keeps aria-expanded truthful and wires Escape — it is never
   what makes a link reachable. */

.nav{display:flex;align-items:stretch;gap:clamp(.5rem,1.2vw,1.25rem);margin-left:auto}
.nav__group{position:relative;display:flex;align-items:stretch}

.nav__top{
  display:inline-flex;align-items:center;gap:6px;
  padding:.4rem .1rem;
  font-family:var(--sans);font-size:.9375rem;font-weight:600;letter-spacing:-.01em;
  color:var(--on-dark);white-space:nowrap;   /* 16.0:1 on --plate */
  position:relative;
  transition:color .15s ease;
}
.nav__top:hover,.nav__group:focus-within .nav__top{color:#fff}
.nav__top.is-current{color:var(--gold)}     /* 7.3:1 on --plate */
.nav__top .nav__chev{width:15px;height:15px;flex:none;color:var(--on-dark-mut);transition:transform .2s var(--ease)}
.nav__top::after{
  content:"";position:absolute;left:0;right:100%;bottom:.1rem;height:3px;border-radius:2px;
  background:var(--gold);transition:right .28s var(--ease);
}
.nav__top:hover::after,.nav__top.is-current::after{right:0}
.nav__group:focus-within .nav__top::after{right:0}
.nav__group:hover .nav__chev,
.nav__group:focus-within .nav__chev{transform:rotate(180deg);color:var(--gold)}

.nav__panel{
  position:absolute;top:100%;left:50%;transform:translate(-50%,6px);
  z-index:70;
  min-width:230px;
  padding:10px;
  /* Deliberately dark. A white card hanging off a near-black bar reads as a
     leftover from the old light header; on --plate-2 the panel, the bar, the
     drawer and the footer are one continuous piece of chrome. */
  background:var(--plate-2);
  border:1px solid var(--plate-line-2);
  border-radius:var(--radius-lg);
  box-shadow:0 24px 48px -20px rgba(0,0,0,.85);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .16s ease,transform .16s ease,visibility .16s;
}
/* A hover bridge across the gap, so the pointer can travel from the trigger
   into the panel without falling through the crack. */
.nav__panel::before{content:"";position:absolute;left:0;right:0;top:-10px;height:10px}
.nav__group:hover > .nav__panel,
.nav__group:focus-within > .nav__panel{
  opacity:1;visibility:visible;pointer-events:auto;transform:translate(-50%,0);
}
/* Escape sets this; cleared as soon as the pointer or focus leaves. */
.nav__group.is-dismissed > .nav__panel{opacity:0;visibility:hidden;pointer-events:none}

.nav__list{display:grid;gap:1px}
.nav__panel--wide{min-width:min(560px,86vw)}
.nav__panel--wide .nav__list{grid-template-columns:1fr 1fr;gap:1px 6px}
@media (min-width:1200px){ .nav__panel--wide{min-width:640px} .nav__panel--wide .nav__list{grid-template-columns:repeat(3,1fr)} }

.nav__list a{
  display:block;
  padding:.5rem .7rem;
  border-radius:var(--radius);
  font-size:.9375rem;font-weight:500;line-height:1.35;
  color:var(--on-dark-mut);text-decoration:none;   /* 8.5:1 on --plate-2 */
  transition:background-color .13s ease,color .13s ease;
}
.nav__list a:hover{background:rgba(212,157,17,.15);color:#fff}
.nav__list a[aria-current="page"]{
  background:rgba(212,157,17,.16);color:var(--gold);font-weight:600;
  box-shadow:inset 3px 0 0 var(--gold);
}

.hdr__phone{
  display:inline-flex;align-items:center;gap:8px;
  text-decoration:none;font-size:.9375rem;font-weight:600;color:var(--on-dark);
  white-space:nowrap;
  transition:color .15s ease;
}
.hdr__phone svg{width:16px;height:16px;color:var(--gold)}
.hdr__phone:hover{color:var(--gold)}

.hdr .btn{min-height:44px;padding:11px 18px;font-size:.9375rem}
/* The 2px inset shadow under the gold fill was a lift off white paper. On the
   dark bar it just muddies the bottom edge, so the button sits flat instead. */
.hdr .btn--gold{box-shadow:none}
.hdr .btn--gold:hover{background:var(--gold-hover);border-color:var(--gold-hover);color:var(--ink)}

.burger{
  display:none;width:44px;height:44px;place-items:center;margin-left:auto;
  border-radius:var(--radius);border:1px solid var(--plate-line-2);background:transparent;
  color:var(--on-dark);
}
.burger span{display:block;width:18px;height:2px;background:var(--on-dark);position:relative;border-radius:2px}
.burger span::before,.burger span::after{
  content:"";position:absolute;left:0;width:18px;height:2px;background:var(--on-dark);border-radius:2px;
}
.burger span::before{top:-6px}
.burger span::after{top:6px}
.burger:hover{border-color:var(--gold)}
.burger:hover span,.burger:hover span::before,.burger:hover span::after{background:var(--gold)}

/* Below the desktop breakpoint the CTA and the burger sit together on the
   right. Only one of them may own the margin-left:auto or they end up with
   the CTA stranded in the middle of the bar. */
@media (max-width:1140px){
  .nav,.hdr__phone{display:none}
  .burger{display:grid;margin-left:0}
  .hdr__in > .btn{margin-left:auto}
}
@media (max-width:640px){
  .hdr__in > .btn{display:none}
  .burger{margin-left:auto}
}
/* No dropdowns exist below the desktop breakpoint — the drawer carries them. */
@media (max-width:1140px){ .nav__panel{display:none} }

/* mobile drawer */
.drawer{
  position:fixed;inset:0;z-index:80;
  background:var(--plate);color:var(--on-dark);
  display:flex;flex-direction:column;justify-content:flex-start;
  padding:calc(var(--gutter) + 3.5rem) var(--gutter) var(--gutter);
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  transform:translateY(-100%);
  transition:transform .4s var(--ease);
}
.drawer.is-open{transform:none}
/* Closed, the drawer is slid off-canvas by a transform — which leaves every
   link in it focusable. At desktop width that means tabbing past the CTA
   walked a keyboard user into twenty invisible links. visibility:hidden takes
   the whole subtree out of the tab order, flips to visible the instant the
   panel opens, and only flips back once the slide-out has finished. Scoped to
   .js, so the scriptless in-flow menu below is untouched. */
.js .drawer{visibility:hidden;transition:transform .4s var(--ease),visibility 0s linear .4s}
.js .drawer.is-open{visibility:visible;transition:transform .4s var(--ease),visibility 0s}

/* No script: the burger cannot open anything, so the drawer stops being an
   off-canvas panel and becomes an ordinary menu block in the flow, right
   under the header. Above the desktop breakpoint .nav already covers it. */
html:not(.js) .burger,
html:not(.js) .drawer__close{display:none}
html:not(.js) .drawer{
  position:static;transform:none;overflow:visible;
  padding:2rem var(--gutter);
  border-bottom:1px solid var(--plate-line);
}
@media (min-width:1141px){ html:not(.js) .drawer{display:none} }
.drawer .wrap{width:100%}
.drawer__group{margin-bottom:1.5rem}
.drawer__head{
  font-family:var(--display);font-weight:700;
  font-size:.6875rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--gold);
  margin:0 0 .5rem;
  padding-bottom:.5rem;
  border-bottom:1px solid var(--plate-line);
}
.drawer__item{display:block}
.drawer a{
  font-family:var(--display);font-weight:600;
  font-size:1.0625rem;letter-spacing:-.02em;
  color:var(--on-dark);text-decoration:none;
  padding-block:.4rem;display:block;
}
.drawer a:hover{color:var(--gold)}
.drawer a[aria-current="page"]{color:var(--gold)}
@media (min-width:560px){
  .drawer .wrap{display:grid;grid-template-columns:1fr 1fr;gap:0 2.5rem;align-items:start}
  .drawer__group:nth-child(2){grid-row:span 2}
  .drawer > .wrap > .drawer__item:last-child{grid-column:1/-1}
}
/* `.drawer a` is more specific than `.btn`, so the button has to take its
   own layout back explicitly or it collapses to a block and the chip wraps. */
.drawer .btn{
  display:inline-flex;align-items:center;justify-content:center;
  width:auto;padding:13px 22px;font-size:1rem;
}
.drawer .btn--gold,.drawer .btn--gold:hover{color:var(--ink)}
.drawer__close{
  position:absolute;top:1rem;right:var(--gutter);z-index:2;
  width:44px;height:44px;border-radius:var(--radius);
  border:1px solid var(--plate-line-2);
  color:var(--on-dark);font-size:1.75rem;line-height:1;
}
.drawer__close:hover{border-color:var(--gold);color:var(--gold)}

/* --- 8. Hero ------------------------------------------------------------- */

.hero{
  position:relative;isolation:isolate;overflow:hidden;
  min-height:clamp(460px,72vh,720px);
  display:grid;align-items:center;
  background:var(--dark);color:var(--on-dark);
}
.hero__bg{position:absolute;inset:0;z-index:-2;overflow:hidden}
.hero__bg img{width:100%;height:100%;object-fit:cover;object-position:center 60%;filter:saturate(.78) contrast(1.02)}
/* Warm scrim.
   The mid-stops used to be .74 (below 1100px) and .80 (above). Sampling the
   composited pixels behind the type, rather than trusting the token table,
   showed that was not enough on the brighter hero photographs: the gold
   .eyebrow is 11px — small text, so it owes 4.5:1 — and it measured as low as
   3.83:1 on eight pages (about, contact, reviews, tile, vinyl, pompano-beach,
   fort-lauderdale, west-palm-beach) at 320/390/768/1024. The white h1 and
   lede were never at risk; only the small gold label was.
   Deepening the mid-stops fixes every one of them at the source and keeps
   --gold exactly #D49D11, rather than introducing a second, lighter gold for
   one label. Re-measured across 23 pages x 7 widths x hero and CTA band:
   294 samples, 0 below 4.5:1, worst case now 4.84:1. */
.hero::after{
  content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(180deg,rgba(26,21,18,.90) 0%,rgba(26,21,18,.83) 48%,rgba(26,21,18,.93) 100%);
}
@media (min-width:1100px){
  /* The copy sits on the dense left end and the booking card on the open
     right end, so this gradient can stay lighter where the photograph
     actually shows. */
  .hero::after{
    background:linear-gradient(95deg,rgba(26,21,18,.94) 0%,rgba(26,21,18,.86) 48%,rgba(26,21,18,.62) 100%);
  }
}
.hero__in{
  position:relative;z-index:1;
  width:100%;max-width:var(--maxw);margin-inline:auto;
  padding:clamp(3.25rem,8vh,5.5rem) var(--gutter) clamp(3.5rem,8vh,5.5rem);
  display:flex;flex-direction:column;align-items:flex-start;text-align:left;
}
.hero .eyebrow{color:var(--gold)}
.hero__title{
  color:#fff;margin:0 0 1rem;
  font-size:clamp(1.9rem,5.6vw,3.15rem);
  line-height:1.1;letter-spacing:-.034em;
  max-width:20ch;
}
.hero__lede{
  color:var(--on-dark);font-size:1rem;line-height:1.62;
  max-width:56ch;margin:0 0 1.75rem;text-wrap:pretty;
}
@media (min-width:900px){ .hero__lede{font-size:1.0625rem} }
.hero .btn-row{justify-content:flex-start}
.hero__note{
  margin:1.25rem 0 0;
  padding:12px 16px;
  border-left:5px solid var(--gold);
  border-radius:0 var(--radius) var(--radius) 0;
  background:rgba(255,255,255,.075);
  color:#fff;font-size:.9375rem;font-weight:500;line-height:1.5;
  max-width:48ch;
}

/* scroll cue */
.hero__cue{
  position:absolute;left:50%;bottom:1.25rem;transform:translateX(-50%);
  z-index:1;color:var(--on-dark-mut);font-size:.625rem;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;
  display:flex;flex-direction:column;align-items:center;gap:.55rem;
}
.hero__cue span{
  display:block;width:2px;height:30px;border-radius:2px;
  background:linear-gradient(to bottom,var(--gold),transparent);
  animation:cue 2.4s var(--ease) infinite;
}
@keyframes cue{
  0%{transform:scaleY(0);transform-origin:top}
  45%{transform:scaleY(1);transform-origin:top}
  55%{transform:scaleY(1);transform-origin:bottom}
  100%{transform:scaleY(0);transform-origin:bottom}
}
@media (max-width:900px){ .hero__cue{display:none} }

/* interior page hero — same banner, shorter */
.hero--page{min-height:clamp(360px,50vh,520px)}
.hero--page .hero__title{font-size:clamp(1.75rem,4.6vw,2.75rem)}

/* --- 8b. Hero booking card ----------------------------------------------- */
/* A compact, SECONDARY conversion path that rides the right half of the hero
   from 1100px up. Four fields and a button; the full form on contact.html is
   the primary path and this must never compete with it.

   Below 1100px the card is not rendered at all. That is deliberate: a phone
   hero that turns into a wall of inputs buries the headline, and the hero's
   own buttons already point at contact.html, where the real form lives. The
   markup stays in the document either way, so nothing here depends on JS. */

.hero__grid{width:100%;display:flex;flex-direction:column;align-items:flex-start}
.hero__copy{width:100%;min-width:0;display:flex;flex-direction:column;align-items:flex-start}
.hero__formcol{display:none}

@media (min-width:1100px){
  /* 1fr for the copy against a fixed 400px card. Both tracks sit inside
     .hero__in, which is max-width:--maxw with one symmetric --gutter padding
     declaration, so the left gutter equals the right gutter by construction at
     every width — there is no second centring rule that could drift. */
  .hero__grid{
    display:grid;
    grid-template-columns:minmax(0,1fr) 400px;
    gap:clamp(2rem,3.6vw,3.5rem);
    align-items:center;
  }
  .hero__formcol{display:block}
  /* The card is the tallest thing in the composition, so a hero carrying one
     needs less of its own padding and a taller floor to stay balanced. */
  .hero--form .hero__in{padding-block:clamp(2.5rem,5.5vh,3.75rem)}
  .hero--form.hero--page{min-height:clamp(540px,60vh,640px)}
  .hero--form .hero__title{max-width:16ch}
  .hero--form .hero__lede{max-width:46ch}
  .hero--form .hero__note{max-width:40ch}
}

.heroform{
  background:var(--paper);
  border:1px solid rgba(26,21,18,.10);
  border-radius:var(--radius-lg);
  padding:1.35rem 1.35rem 1.4rem;
  box-shadow:var(--shadow-lg);
  color:var(--body);
}
.heroform__h{
  font-family:var(--display);font-weight:600;
  font-size:1.125rem;line-height:1.2;letter-spacing:-.028em;
  color:var(--ink);margin:0 0 .3rem;
}
.heroform__sub{font-size:.875rem;line-height:1.45;color:var(--muted);margin:0 0 1rem}
.heroform__row{display:grid;grid-template-columns:1fr 1fr;gap:0 .75rem}
.heroform .field{margin-bottom:.8rem}
.heroform .field label{margin-bottom:.3rem}
.heroform .field input{padding:9px 11px}

/* The site's one qualifier. Body size, ink colour, full strength — never
   shrunk to grey fine print, and never moved out of the card. */
.heroform__min{
  margin:.2rem 0 1rem;
  padding:.6rem .8rem;
  border-left:4px solid var(--gold);
  border-radius:0 var(--radius) var(--radius) 0;
  background:var(--tint);
  color:var(--ink);
  font-size:.9375rem;line-height:1.45;
}
.heroform__min strong{font-weight:600}
.heroform__go{width:100%;min-height:48px}
.heroform__note{margin:.75rem 0 0;font-size:.8125rem;line-height:1.45;color:var(--gold-ink)}
.heroform__micro{margin:.7rem 0 0;font-size:.8125rem;line-height:1.45;color:var(--body)}

/* .hero :focus-visible switches the ring to --gold so it reads on the dark
   band. On the light card that same gold measures 2.2:1, so the card takes
   the darker gold back. */
.hero .heroform :focus-visible{outline-color:var(--gold-hover)}

/* --- 9. Fact ticker ------------------------------------------------------ */
/* A drifting strip of selling points under the hero, on the gold band. */

/* The marquee track is deliberately far wider than the screen, so the strip
   must clip it. This used to be masked by overflow-x:hidden on <html>, which
   was silently killing the sticky header; with that removed the clip has to
   be real and anchored to the viewport, not to a parent that has already
   grown to fit the track. */
.facts{background:var(--gold);color:var(--ink);overflow:hidden;max-width:100vw}
.facts__in{
  display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:.4rem clamp(1rem,3vw,2.5rem);
  padding-block:1rem;
  font-size:.9375rem;font-weight:600;text-align:center;
}
.facts__in b{font-weight:700}
.facts__dot{width:5px;height:5px;border-radius:50%;background:rgba(26,21,18,.45);flex:none}

.marquee{overflow:hidden;position:relative;max-width:100%;--speed:38s}
/* Static, wrapped and complete by default: the -50% loop only works once JS
   has appended the second copy of the track, so nothing animates until it has.
   With JS off, or under reduced motion, this IS the finished state. */
.marquee__track{display:flex;flex-wrap:wrap;justify-content:center;width:auto}
.marquee.is-loop .marquee__track{
  flex-wrap:nowrap;justify-content:flex-start;width:max-content;
  animation:marq var(--speed) linear infinite;
}
.marquee.is-loop:hover .marquee__track,
.marquee.is-loop:focus-within .marquee__track{animation-play-state:paused}
.marquee.is-static .marquee__track{animation:none;width:auto;flex-wrap:wrap;justify-content:center}
.marquee__item{
  display:inline-flex;align-items:center;
  gap:clamp(1rem,2.5vw,2.25rem);
  padding-right:clamp(1rem,2.5vw,2.25rem);
  white-space:nowrap;
  font-size:.875rem;font-weight:600;letter-spacing:-.01em;
}
.facts .marquee{padding-block:.85rem}
@media (min-width:900px){ .marquee__item{font-size:.9375rem} }
@keyframes marq{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* --- 10. Grids ----------------------------------------------------------- */

.grid{display:grid;gap:clamp(1rem,2vw,1.5rem)}
.grid > *{min-width:0}
.g2{grid-template-columns:repeat(2,minmax(0,1fr))}
.g3{grid-template-columns:repeat(3,minmax(0,1fr))}
.g4{grid-template-columns:repeat(4,minmax(0,1fr))}
@media (max-width:960px){
  .g3,.g4{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* A three-card row collapsing to two columns leaves the third card alone in
     a half-width cell with a hole beside it. An odd trailing item takes the
     whole row instead, so no row is ever left half-empty. */
  .g2 > :last-child:nth-child(odd),
  .g3 > :last-child:nth-child(odd),
  .g4 > :last-child:nth-child(odd){grid-column:1/-1}
}
@media (max-width:620px){ .g2,.g3,.g4{grid-template-columns:1fr} }

/* the old staggered row: everything sits on one baseline now */
.offset > *:nth-child(even){margin-top:0}

.mgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(.75rem,1.4vw,1.15rem)}
@media (max-width:760px){ .mgrid{grid-template-columns:repeat(2,minmax(0,1fr))} }

/* paired column headings align across the row */
@media (min-width:821px){ .pair-h{min-height:2.5em;display:flex;align-items:flex-end} }

/* --- 11. Cards ----------------------------------------------------------- */

/* image over, copy under — the workhorse service card */
.vcard{
  display:flex;flex-direction:column;height:100%;text-decoration:none;
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  overflow:hidden;
  box-shadow:var(--shadow);
  transition:border-color .2s ease,box-shadow .2s ease,transform .2s ease;
}
.vcard:hover{border-color:var(--line-2);box-shadow:var(--shadow-lg);transform:translateY(-2px)}
.vcard__media{display:block;overflow:hidden;aspect-ratio:4/3;background:var(--sand)}
.vcard__media img{width:100%;height:100%;object-fit:cover;transition:transform .6s var(--ease)}
.vcard:hover .vcard__media img{transform:scale(1.045)}
.vcard h3{font-size:1.125rem;margin:1.1rem 1.15rem .5rem;color:var(--ink)}
.vcard p{font-size:.9375rem;line-height:1.6;margin:0 1.15rem;color:var(--body)}
/* The arrow used to be a second flex item beside the label. In the four-column
   city grid the card's inner width drops to about 175px while "Flooring in
   West Palm Beach" wants 237px, so the label wrapped to two lines and left the
   arrow marooned in the vertical middle beside them — visible on Fort
   Lauderdale, West Palm Beach and Pompano Beach from 1024px up.
   `white-space:nowrap` is not the fix: .vcard sets overflow:hidden, so the
   arrow would simply be clipped off instead. Making the link a block and the
   arrow an inline box puts the arrow back into the text flow, where it follows
   the last word onto whichever line that word lands on. */
.vcard__go{
  display:block;
  margin:1.1rem 1.15rem 1.25rem;padding-bottom:3px;
  align-self:flex-start;margin-top:auto;
  font-size:.9375rem;font-weight:600;color:var(--gold-ink);
  border-bottom:2px solid var(--gold);
  text-wrap:balance;   /* two even lines, not five words and an orphan */
}
.vcard__go svg{
  width:15px;height:15px;
  display:inline-block;vertical-align:-2px;margin-left:8px;
  transition:transform .2s var(--ease);
}
.vcard:hover .vcard__go svg{transform:translateX(4px)}
.vcard:hover .vcard__go{color:var(--ink);border-bottom-color:var(--ink)}
.band-ink .vcard{background:var(--dark-2);border-color:var(--line-dark)}
.band-ink .vcard h3{color:var(--on-dark)}
.band-ink .vcard p{color:var(--on-dark-mut)}
.band-ink .vcard__go{color:var(--gold);border-bottom-color:var(--gold)}

/* caption-bar project card */
.tcard{
  position:relative;display:block;overflow:hidden;
  border-radius:var(--radius-lg);aspect-ratio:4/3;
  text-decoration:none;background:var(--dark-2);
  border:1px solid var(--line);
}
.tcard img{width:100%;height:100%;object-fit:cover;transition:transform .7s var(--ease)}
.tcard:hover img{transform:scale(1.05)}
/* A flat plate rather than a fade. A fade tall enough to cover two lines of
   caption leaves the second line sitting on roughly half-strength scrim, and
   over a bright photograph that is where the contrast goes. The plate is a
   constant .92 behind every glyph; the fade is moved above the bar, where
   there is no text to fail. */
.tcard__bar{
  position:absolute;left:0;right:0;bottom:0;
  display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:1rem 1.15rem;color:#fff;
  background:rgba(12,10,8,.92);
}
.tcard__bar::before{
  content:"";position:absolute;left:0;right:0;bottom:100%;height:56px;
  background:linear-gradient(to top,rgba(12,10,8,.92),rgba(12,10,8,0));
  pointer-events:none;
}
.tcard__bar b{display:block;font-family:var(--display);font-weight:700;font-size:1.0625rem;letter-spacing:-.025em;line-height:1.25}
.tcard__bar span{font-size:.8125rem;color:var(--on-dark-mut)}
.tcard__go{
  width:34px;height:34px;border-radius:50%;flex:none;
  background:rgba(255,255,255,.18);
  display:grid;place-items:center;
  transition:background-color .2s ease,transform .2s ease,color .2s ease;
}
.tcard__go svg{width:14px;height:14px}
.tcard:hover .tcard__go{background:var(--gold);color:var(--ink);transform:translateX(3px)}

/* full-bleed portrait service card */
.scard{
  position:relative;display:block;text-decoration:none;
  border-radius:var(--radius-lg);overflow:hidden;
  aspect-ratio:3/4;background:var(--dark-2);
  border:1px solid var(--line);
}
.scard img{width:100%;height:100%;object-fit:cover;transition:transform .7s var(--ease)}
.scard:hover img{transform:scale(1.045)}
.scard__veil{
  position:absolute;inset:0;
  background:linear-gradient(to top,rgba(12,10,8,.95) 0%,rgba(12,10,8,.86) 30%,rgba(12,10,8,.45) 62%,rgba(12,10,8,.04) 100%);
}
.scard__body{position:absolute;inset:auto 0 0 0;padding:1.35rem 1.25rem}
.scard__body h3{color:#fff;font-size:1.2rem;margin-bottom:.4rem}
.scard__body p{color:var(--on-dark-mut);font-size:.875rem;line-height:1.5;margin:0}
.scard__more{
  display:inline-flex;align-items:center;gap:8px;margin-top:.9rem;
  color:var(--gold);font-size:.75rem;letter-spacing:.11em;text-transform:uppercase;font-weight:700;
}
.scard__more svg{width:14px;height:14px;flex:none}

/* magazine-index project card */
.pcard{display:block;text-decoration:none}
/* display:block is load-bearing. .pcard__media is a <span>, and an INLINE box
   ignores aspect-ratio entirely — the ratio below was dead, and the frame took
   whatever height the <img> inside it happened to be. It went unnoticed while
   the only .pcard images were portrait project photos close to 3/4 anyway; the
   catalogue's landscape swatches made it obvious, rendering the collection
   cards as ~1000px vertical strips. */
.pcard__media{display:block;position:relative;border-radius:var(--radius-lg);overflow:hidden;aspect-ratio:3/4;background:var(--sand)}
.pcard__media img{width:100%;height:100%;object-fit:cover;transition:transform .7s var(--ease)}
.pcard:hover .pcard__media img{transform:scale(1.045)}
.pcard__chip{
  position:absolute;right:.7rem;bottom:.7rem;
  background:var(--gold);color:var(--ink);
  padding:.35rem .6rem;border-radius:100px;
  font-size:.625rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
}
.pcard__foot{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;padding-top:.9rem}
.pcard__loc{display:block;color:var(--gold-ink);font-size:.6875rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;margin-bottom:.35rem}
/* Both are <span>s, and an inline box ignores vertical margin: .pcard__meta's
   margin-top did nothing and the two lines overlapped wherever the title wrapped
   to a second line. Block makes the margins real and lets the title wrap. */
.pcard__title{display:block;font-family:var(--display);font-weight:700;font-size:1.125rem;letter-spacing:-.025em;color:var(--ink);line-height:1.24}
.pcard__meta{display:block;font-size:.8125rem;color:var(--muted);margin-top:.35rem}
.pcard__arrow{
  width:34px;height:34px;border-radius:50%;border:1px solid var(--line-2);
  display:grid;place-items:center;flex:none;margin-top:.2rem;
  transition:background-color .2s ease,border-color .2s ease,transform .2s ease,color .2s ease;
}
.pcard__arrow svg{width:14px;height:14px}
.pcard:hover .pcard__arrow{border-color:var(--gold);background:var(--gold);color:var(--ink);transform:translateX(3px)}

/* differentiator card, usually on the dark band */
.dcard{
  padding:1.6rem 1.5rem 1.75rem;
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow);
}
.dcard__ico{
  width:52px;height:52px;border-radius:50%;
  background:var(--gold);color:var(--ink);
  display:grid;place-items:center;
  margin-bottom:1.15rem;
}
.dcard__ico svg{width:26px;height:26px;stroke-width:1.7}
.dcard h3{font-size:1.125rem;margin-bottom:.55rem;color:var(--ink)}
.dcard p{font-size:.9375rem;line-height:1.62;color:var(--body);margin:0}
.band-ink .dcard{background:var(--dark-2);border-color:var(--line-dark);box-shadow:none}
.band-ink .dcard h3{color:var(--on-dark)}
.band-ink .dcard p{color:var(--on-dark-mut)}

/* review card */
.rcard{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:1.4rem 1.35rem;display:flex;flex-direction:column;gap:.85rem;height:100%;
  box-shadow:var(--shadow);
}
.rcard__stars{color:var(--gold);font-size:1rem;letter-spacing:.1em;line-height:1}
.rcard__text{font-size:.9375rem;line-height:1.62;color:var(--body);flex:1;margin:0}
.rcard__who{display:flex;align-items:center;gap:.75rem;padding-top:.85rem;border-top:1px solid var(--line)}
.rcard__ava{width:40px;height:40px;border-radius:50%;object-fit:cover;flex:none;background:var(--sand)}
.rcard__name{font-size:.9375rem;color:var(--ink);font-weight:600;line-height:1.3}
.rcard__meta{font-size:.75rem;color:var(--muted)}

.gscore{
  display:inline-flex;align-items:center;gap:1rem;flex-wrap:wrap;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:.9rem 1.35rem;box-shadow:var(--shadow);
}
.gscore__n{font-family:var(--display);font-weight:700;font-size:2rem;letter-spacing:-.04em;color:var(--ink);line-height:1;font-variant-numeric:tabular-nums}

/* Editorial beat: a copy column beside a portrait figure.

   Every photograph in this library is a 9:16 phone frame, so the figure
   column is narrow and tall by design and the copy takes the wider side.
   Nothing here ever stretches a portrait frame into a banner. `--flip` puts
   the picture first on desktop; on mobile the copy always leads. */
.beat{
  display:grid;
  grid-template-columns:minmax(0,1.12fr) minmax(0,.88fr);
  gap:clamp(1.75rem,4.5vw,4rem);
  /* Centred, not top-aligned: a copy column shorter than a 9:13 portrait used
     to sit at the top of the frame with a bank of dead space beneath it. */
  align-items:center;
}
/* Inside a beat the portrait is height-capped for the same reason. Standalone
   .figp frames (photostrips, single figures) are untouched. */
.beat__fig img{max-height:clamp(24rem,46vw,34rem)}
.beat__copy > * + *{margin-top:1.05em}
.beat__copy .eyebrow{margin-bottom:0}
.beat__copy .lede{color:var(--muted)}
.band-ink .beat__copy .lede{color:var(--on-dark-mut)}
/* Flipping the beat must flip the COLUMN WIDTHS too, or the picture ends up
   in the wide column and the copy in the narrow one. */
.beat--flip{grid-template-columns:minmax(0,.88fr) minmax(0,1.12fr)}
.beat--flip .beat__copy{order:2}
.beat--flip .beat__fig{order:1}
@media (max-width:860px){
  .beat,.beat--flip{grid-template-columns:1fr;gap:clamp(1.75rem,4vw,2.5rem)}
  .beat--flip .beat__copy{order:1}
  .beat--flip .beat__fig{order:2}
  .beat__fig{max-width:26rem;margin-inline:auto}
  /* Once the beat is a single column the figure gets the full column width,
     but the desktop height cap stayed, so a 9:16 phone portrait was squeezed
     into a box wide enough to be nearly square. object-fit:cover then took
     the difference off the top and bottom: only ~52% of the frame survived at
     tablet widths, and on the customer photos that is exactly where the faces
     are. Dropping the cap and letting the 9:13 ratio set the height puts the
     whole subject back in frame. */
  .beat__fig img{max-height:none}
}

/* A captioned portrait frame. Used on its own and inside .photostrip. */
.figp{margin:0}
.figp img{
  width:100%;aspect-ratio:3/4;object-fit:cover;
  border-radius:var(--radius-img);
  background:var(--sand);
  border:1px solid var(--line);
}
.band-ink .figp img{border-color:var(--line-dark)}
.figp--tall img{aspect-ratio:9/13}
/* A phone photo of a ROOM puts the floor in the bottom half of the frame, so
   a centre crop lands on the wall. Floor-forward frames get pushed down. */
.figp--floor img{object-position:center 68%}
.figp figcaption{
  margin-top:.85rem;
  font-size:.8125rem;line-height:1.5;color:var(--muted);
  text-wrap:pretty;
}
.band-ink .figp figcaption{color:var(--on-dark-mut)}

/* An evenly-spread row of portrait frames. Column count is explicit so a
   trailing item can never be orphaned mid-row. */
.photostrip{display:grid;gap:clamp(.75rem,1.6vw,1.25rem)}
.photostrip--2{grid-template-columns:repeat(2,minmax(0,1fr))}
.photostrip--3{grid-template-columns:repeat(3,minmax(0,1fr))}
.photostrip--4{grid-template-columns:repeat(4,minmax(0,1fr))}
/* A portrait frame stays legible at a quarter of a tablet width, so the 3-up
   holds three columns all the way down rather than dropping to a 2 + 1 row
   with a hole in it. The 4-up halves to a clean 2 x n. */
@media (max-width:860px){ .photostrip--4{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:560px){ .photostrip--3{grid-template-columns:1fr;max-width:22rem;margin-inline:auto} }
@media (max-width:460px){ .photostrip--2,.photostrip--4{grid-template-columns:1fr;max-width:22rem;margin-inline:auto} }

/* The 6th cell of the homepage service grid: a card that is a call to action
   rather than a service, so the 3-column row is never left with a two-cell
   hole. It matches the .vcard silhouette exactly. */
.ccard{
  display:flex;flex-direction:column;justify-content:center;
  height:100%;text-decoration:none;
  padding:1.75rem 1.5rem;
  background:var(--plate);
  border:1px solid var(--plate);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow);
  transition:box-shadow .2s ease,transform .2s ease;
}
.ccard:hover{box-shadow:var(--shadow-lg);transform:translateY(-2px)}
.ccard .eyebrow{color:var(--gold);margin-bottom:1rem}
.ccard h3{color:var(--on-dark);font-size:1.25rem;margin-bottom:.6rem}
.ccard p{color:var(--on-dark-mut);font-size:.9375rem;line-height:1.6;margin:0 0 1.5rem}
.ccard .btn{align-self:flex-start}

/* --- 12. Numbered steps -------------------------------------------------- */

.step{
  display:grid;grid-template-columns:auto 1fr;gap:1.15rem;
  padding:1.5rem 0;
  border-top:1px solid var(--line);
}
.step:last-child{border-bottom:1px solid var(--line)}
.band-ink .step,.band-ink .step:last-child{border-color:var(--line-dark)}
.step__n{
  display:inline-flex;align-items:center;justify-content:center;
  width:42px;height:42px;flex:none;
  border-radius:10px;
  background:var(--tint);border:1px solid var(--gold-soft);
  color:var(--gold-ink);
  font-family:var(--display);font-weight:700;font-size:.9375rem;line-height:1;
  font-variant-numeric:tabular-nums;letter-spacing:0;
}
.band-ink .step__n{background:rgba(212,157,17,.16);border-color:rgba(212,157,17,.4);color:var(--gold)}
.step h3{font-size:1.0625rem;margin-bottom:.45rem}
.step p{margin:0;font-size:.9375rem;line-height:1.62}
@media (max-width:620px){ .step{grid-template-columns:1fr;gap:.75rem} }

/* Term / description rows. Was an inline flex with a 14rem min-width on the
   term, which could not fit at 320px and pushed the description off the edge
   of the screen (invisibly, because html carries overflow-x:hidden). As a
   grid it stacks instead. */
.deflist{border-top:1px solid var(--line)}
.deflist li{
  display:grid;grid-template-columns:14rem minmax(0,1fr);gap:.25rem 1rem;
  padding-block:.9rem;border-bottom:1px solid var(--line);
}
.band-ink .deflist,.band-ink .deflist li{border-color:var(--line-dark)}
.deflist strong{color:var(--ink);font-weight:500}
.band-ink .deflist strong{color:var(--on-dark)}
@media (max-width:560px){ .deflist li{grid-template-columns:1fr} }

/* The contact page's form / sidebar split. It used to carry its two columns
   in an inline style with no breakpoint, so at 390px the form was crushed
   into a third of the screen and the email address ran off the edge. */
.formsplit{
  display:grid;
  grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);
  gap:clamp(2rem,5vw,4.5rem);
  align-items:start;
}
@media (max-width:860px){ .formsplit{grid-template-columns:1fr} }

/* --- 13. Tables ---------------------------------------------------------- */

.tscroll{overflow-x:auto;-webkit-overflow-scrolling:touch}

.ptable{width:100%;border-collapse:collapse;font-size:.9375rem}
.ptable th,.ptable td{text-align:left;padding:.85rem .9rem .85rem 0;border-bottom:1px solid var(--line)}
.ptable thead th{
  font-size:.6875rem;font-weight:700;letter-spacing:.11em;text-transform:uppercase;
  color:var(--muted);border-bottom:2px solid var(--ink);white-space:nowrap;
  padding-bottom:.7rem;
}
.ptable tbody td:first-child{color:var(--ink);font-weight:600}
.ptable tbody tr:last-child td{border-bottom:0}
.ptable tbody tr:hover{background:var(--tint)}
.band-ink .ptable th,.band-ink .ptable td{border-bottom-color:var(--line-dark);color:var(--on-dark)}
.band-ink .ptable thead th{color:var(--on-dark-mut);border-bottom-color:var(--gold)}

/* --- 14. FAQ ------------------------------------------------------------- */

.faq{display:grid;gap:10px}
.faq__item{
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow);
  overflow:hidden;
}
.faq__item:has(.faq__q[aria-expanded="true"]){border-color:var(--gold-soft)}
.faq__q{
  width:100%;display:flex;align-items:center;justify-content:space-between;gap:18px;
  padding:16px 18px;text-align:left;
  font-family:var(--display);font-weight:600;
  font-size:1.0625rem;line-height:1.4;letter-spacing:-.018em;
  color:var(--ink);
}
.faq__q:hover{color:var(--gold-ink)}
.faq__ico{
  width:28px;height:28px;border-radius:50%;
  background:var(--gold);
  display:grid;place-items:center;flex:none;position:relative;
  transition:transform .2s var(--ease);
}
.faq__ico::before,.faq__ico::after{content:"";position:absolute;background:var(--ink);border-radius:2px}
.faq__ico::before{width:12px;height:2.5px}
.faq__ico::after{width:2.5px;height:12px;transition:opacity .18s ease}
.faq__q[aria-expanded="true"] .faq__ico{transform:rotate(180deg)}
.faq__q[aria-expanded="true"] .faq__ico::after{opacity:0}
/* Open by default. Only a running script is allowed to collapse an answer,
   so with JS off every answer is on the page and readable. */
.faq__a{overflow:hidden}
.js .faq__a{height:0;transition:height .32s var(--ease)}
.faq__a > div{padding:0 18px 18px;max-width:70ch}
.faq__a p{font-size:1rem;line-height:1.65;color:var(--body)}
.band-ink .faq__item{background:var(--dark-2);border-color:var(--line-dark);box-shadow:none}
.band-ink .faq__q{color:var(--on-dark)}
.band-ink .faq__q:hover{color:var(--gold)}
.band-ink .faq__a p{color:var(--on-dark-mut)}

/* --- 15. Before / after slider ------------------------------------------- */

.ba{
  position:relative;border-radius:var(--radius-lg);overflow:hidden;
  aspect-ratio:4/5;background:var(--dark-2);
  border:1px solid var(--line);
  user-select:none;touch-action:pan-y;
}
.ba img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.ba__after{clip-path:inset(0 0 0 var(--pos,50%))}
.ba__line{position:absolute;top:0;bottom:0;left:var(--pos,50%);width:3px;margin-left:-1.5px;background:var(--gold);pointer-events:none}
.ba__grab{
  position:absolute;top:50%;left:var(--pos,50%);transform:translate(-50%,-50%);
  width:44px;height:44px;border-radius:50%;
  background:var(--gold);color:var(--ink);
  display:grid;place-items:center;
  box-shadow:0 6px 18px -6px rgba(0,0,0,.55);
  pointer-events:none;
}
.ba__grab::before{
  content:"";width:14px;height:14px;
  border-left:2.5px solid currentColor;border-right:2.5px solid currentColor;
}
.ba__tag{
  position:absolute;top:.8rem;padding:.3rem .6rem;border-radius:100px;
  background:rgba(12,10,8,.88);color:#fff;
  font-size:.625rem;font-weight:700;letter-spacing:.11em;text-transform:uppercase;
}
.ba__tag--b{left:.8rem}
.ba__tag--a{right:.8rem}
.ba input{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:ew-resize;margin:0}

/* --- 16. Gallery --------------------------------------------------------- */

/* A uniform grid, not CSS columns. `columns` balances by HEIGHT, so the last
   items land wherever the shortest column is and the block ends with holes
   punched in the middle columns — which is what it was doing. Every photo in
   this gallery is a 9:16 phone frame, so equal cells lose nothing and the
   block ends on a straight edge. A partial last row simply left-aligns. */
.masonry{
  display:grid;
  grid-template-columns:repeat(5,minmax(0,1fr));
  gap:clamp(.75rem,1.5vw,1.15rem);
  align-items:start;
}
.masonry > *{margin:0}
.masonry .gtile img{width:100%;aspect-ratio:9/16;object-fit:cover}
@media (max-width:1180px){ .masonry{grid-template-columns:repeat(4,minmax(0,1fr))} }
@media (max-width:900px){  .masonry{grid-template-columns:repeat(3,minmax(0,1fr))} }
@media (max-width:640px){  .masonry{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:420px){  .masonry{grid-template-columns:1fr;max-width:22rem;margin-inline:auto} }

.gtile{
  position:relative;display:block;overflow:hidden;
  border-radius:var(--radius-img);background:var(--sand);
  border:1px solid var(--line);
}
.gtile img{width:100%;transition:transform .7s var(--ease)}
.gtile:hover img{transform:scale(1.04)}
.gtile__cap{
  position:absolute;inset:auto 0 0 0;padding:1rem .95rem .85rem;
  background:rgba(12,10,8,.92);
  box-shadow:0 -30px 30px -18px rgba(12,10,8,.92);
  color:#fff;opacity:0;transform:translateY(6px);
  transition:opacity .3s var(--ease),transform .3s var(--ease);
}
.gtile:hover .gtile__cap,.gtile:focus-within .gtile__cap{opacity:1;transform:none}
/* Touch devices have no hover, so a caption that only appears on :hover is a
   caption that never appears. The gallery copy promises "every caption is a
   real record", so on coarse pointers they are always on. */
@media (hover: none){
  .gtile__cap{opacity:1;transform:none}
  .gtile img{transform:none !important}
}
.gtile__cap b{display:block;font-family:var(--display);font-weight:700;font-size:.9375rem;letter-spacing:-.02em;line-height:1.25}
.gtile__cap span{font-size:.75rem;color:var(--on-dark-mut)}

.filters{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:1.75rem}
.filters button{
  min-height:40px;padding:.5rem 1rem;
  border-radius:100px;border:1px solid var(--line-2);
  background:var(--paper);
  font-size:.875rem;font-weight:600;color:var(--body);
  transition:background-color .15s ease,border-color .15s ease,color .15s ease;
}
.filters button:hover{border-color:var(--ink);color:var(--ink)}
.filters button[aria-pressed="true"]{background:var(--ink);color:#fff;border-color:var(--ink)}

/* --- 17. CTA photo band -------------------------------------------------- */

.ctaband{position:relative;isolation:isolate;overflow:hidden;background:var(--dark);color:var(--on-dark)}
.ctaband img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:-2;filter:saturate(.78)}
.ctaband::after{
  content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(105deg,rgba(26,21,18,.96) 10%,rgba(26,21,18,.90) 56%,rgba(26,21,18,.80) 100%);
}
.ctaband__in{padding-block:clamp(3.25rem,7vw,5.75rem);max-width:44rem}
.ctaband h2{color:var(--on-dark)}

/* --- 18. Forms ----------------------------------------------------------- */

.field{display:block;margin-bottom:1rem}
.field > span:first-child,.field label{
  display:block;margin-bottom:.4rem;
  font-size:.8125rem;font-weight:600;letter-spacing:-.005em;
  color:var(--ink);text-transform:none;
}
.field input,.field select,.field textarea{
  width:100%;
  font-family:var(--sans);
  font-size:16px;  /* never below 16px: prevents iOS zoom on focus */
  font-weight:400;line-height:1.4;
  color:var(--ink);
  background:var(--paper);
  border:1px solid var(--line-2);
  border-radius:var(--radius);
  padding:11px 12px;
  transition:border-color .14s ease,box-shadow .14s ease;
}
.field select{appearance:none;-webkit-appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--muted) 50%),linear-gradient(135deg,var(--muted) 50%,transparent 50%);background-position:calc(100% - 18px) 21px,calc(100% - 12px) 21px;background-size:6px 6px,6px 6px;background-repeat:no-repeat;padding-right:38px}
.field textarea{resize:vertical;min-height:7rem}
.field input:hover,.field select:hover,.field textarea:hover{border-color:var(--muted)}
.field input:focus,.field select:focus,.field textarea:focus{
  outline:none;border-color:var(--gold-hover);box-shadow:0 0 0 3px rgba(212,157,17,.28);
}
.field input::placeholder,.field textarea::placeholder{color:#7A726B}
.field.is-invalid input,.field.is-invalid select,.field.is-invalid textarea{border-color:var(--err);background:var(--err-bg)}

/* honeypot: off-screen, not display:none, which some bots detect */
.hp{position:absolute !important;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}

/* --- 19. Footer ---------------------------------------------------------- */

/* Same ground as the header, so the top and the bottom of every page are a
   matched pair and the logo lockup sits flush at both ends. */
.ftr{
  background:var(--plate);
  color:var(--on-dark-mut);
  border-top:1px solid var(--plate-line);
  padding-block:clamp(2.75rem,5vw,4.25rem) 1.75rem;
  font-size:.9375rem;
}
.ftr .brand{margin-bottom:1.25rem}
.ftr .brand__logo{height:64px}
@media (min-width:700px){ .ftr .brand__logo{height:76px} }
.ftr__grid{display:grid;grid-template-columns:1.7fr 1fr 1fr 1fr;gap:clamp(1.75rem,4vw,3.25rem)}
@media (max-width:900px){ .ftr__grid{grid-template-columns:1fr 1fr} }
@media (max-width:560px){ .ftr__grid{grid-template-columns:1fr} }
.ftr h3{
  font-family:var(--display);font-weight:700;
  font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--gold);margin-bottom:1rem;
}
.ftr p{color:var(--on-dark)}
.ftr a{color:var(--on-dark-mut);text-decoration:none;font-size:.9375rem;transition:color .15s ease}
.ftr a:hover{color:var(--gold);text-decoration:underline}
.ftr li{margin-bottom:.55rem}
.ftr__contact{margin-top:1.25rem}
.ftr__contact li{display:flex;gap:.7rem;align-items:flex-start;margin-bottom:.85rem}
.ftr__contact svg{width:17px;height:17px;color:var(--gold);flex:none;margin-top:.32rem}
.ftr__contact a{font-size:1rem;font-weight:600;color:var(--gold)}
.ftr__contact a:hover{color:var(--on-dark)}
.ftr__bot{
  margin-top:clamp(2.25rem,4.5vw,3.25rem);padding-top:1.4rem;
  border-top:1px solid var(--plate-line);
  display:flex;flex-wrap:wrap;gap:1rem;justify-content:space-between;align-items:center;
  font-size:.8125rem;color:var(--on-dark-mut);
}
.socials{display:flex;gap:.6rem}
.socials a{
  width:38px;height:38px;border-radius:50%;
  border:1px solid var(--plate-line-2);color:var(--on-dark-mut);
  display:grid;place-items:center;
  transition:background-color .15s ease,border-color .15s ease,color .15s ease;
}
.socials a:hover{background:var(--gold);border-color:var(--gold);color:var(--ink)}
.socials svg{width:16px;height:16px}

/* --- 20. Statement blocks ------------------------------------------------ */

/* Oversized display heading over its media. The serif/blend treatment is
   gone; it is now simply a large, centred Poppins title with the media
   beneath it. */
.overlap{position:relative}
.overlap .eyebrow{justify-content:center}
.overlap__title{
  position:relative;z-index:2;text-align:center;
  font-family:var(--display);font-weight:700;line-height:1.1;
  font-size:clamp(1.75rem,4.6vw,2.9rem);letter-spacing:-.034em;
  color:var(--ink);
  margin:0 auto;max-width:22ch;
}
.band-ink .overlap__title{color:var(--on-dark)}
.overlap__media{position:relative;z-index:1;margin-top:clamp(1.75rem,3.4vw,2.75rem)}
.overlap__media img{width:100%;border-radius:var(--radius-img);object-fit:cover}

/* ghost watermark — texture, deliberately almost invisible */
.wm{position:relative;overflow:hidden}
.wm__mark{
  position:absolute;
  font-family:var(--display);font-weight:700;
  font-size:clamp(12rem,26vw,24rem);line-height:.8;letter-spacing:-.05em;
  color:var(--ink);opacity:.03;
  pointer-events:none;user-select:none;
  right:-2vw;bottom:-4vh;z-index:0;
}
.band-ink .wm__mark{color:var(--gold);opacity:.06}
.wm > .wrap{position:relative;z-index:1}
@media (max-width:760px){ .wm__mark{display:none} }

/* stat row */
.stat{
  text-align:center;
  padding:1.35rem 1rem;
  border:1px solid var(--line);border-radius:var(--radius-lg);
  background:var(--paper);
}
.stat__n{
  display:block;
  font-family:var(--display);font-weight:700;
  font-size:clamp(1.85rem,4vw,2.75rem);line-height:1;letter-spacing:-.04em;
  color:var(--ink);font-variant-numeric:tabular-nums;
}
.stat__l{
  display:block;margin-top:.7rem;
  font-size:.75rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted);
}
.band-ink .stat{background:var(--dark-2);border-color:var(--line-dark)}
.band-ink .stat__n{color:var(--gold)}
.band-ink .stat__l{color:var(--on-dark-mut)}

/* --- 20b. Split: a copy column beside a picture --------------------------
   The site's standard "paragraphs on one side, photograph on the other" band.
   Three things it fixes over the ad-hoc `.grid.g2` it replaces:

   1. The copy is CENTRED against the picture (align-items:center), so a short
      three-paragraph column no longer floats at the top of a 700px-tall
      portrait frame with a wall of empty space under it.
   2. The picture is height-capped. A 4/5 frame in a 560px column wants to be
      700px tall, which dwarfs the text next to it; --split-media-h holds it to
      a sane band height and object-fit:cover does the rest.
   3. `.split--flip` puts the picture on the LEFT. The builders alternate this
      automatically down a page, so two consecutive bands never put their
      photographs on the same side.

   On mobile the copy always leads and the picture follows, whichever side it
   was on at desktop. */
.split{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:clamp(2rem,5vw,4.5rem);
  align-items:center;
}
.split__copy{max-width:38rem}
.split__copy > * + *{margin-top:1.05em}
.split--flip .split__copy{justify-self:end}
/* Order, not column swapping: the two tracks are equal, so ordering alone
   moves the picture across without disturbing the measure of the copy. */
.split--flip .split__copy{order:2}
.split--flip .split__media{order:1}

.split__media{margin:0;position:relative}
.split__media img{
  width:100%;display:block;
  aspect-ratio:4/5;
  max-height:var(--split-media-h,clamp(22rem,42vw,32rem));
  object-fit:cover;
  border-radius:var(--radius-img);
  background:var(--sand);
  box-shadow:var(--shadow-lg);
}
/* A phone photo of a room puts the floor low in the frame, so a centre crop
   lands on the wall. Same correction .figp--floor makes. */
.split__media--floor img{object-position:center 68%}
/* The quiet offset plate behind the frame. Pure texture: it reads as a
   deliberate mount rather than a picture dropped on the band. */
.split__media::before{
  content:"";position:absolute;z-index:0;
  inset:auto -14px -14px auto;
  width:min(62%,17rem);height:58%;
  border-radius:var(--radius-img);
  background:var(--tint);
}
.band-ink .split__media::before{background:rgba(212,157,17,.13)}
.split--flip .split__media::before{inset:auto auto -14px -14px}
.split__media img{position:relative;z-index:1}
.split__media figcaption{
  position:relative;z-index:1;
  margin-top:.85rem;font-size:.8125rem;line-height:1.5;color:var(--muted);
  text-wrap:pretty;
}
.band-ink .split__media figcaption{color:var(--on-dark-mut)}

/* A non-photo right-hand column (the duration table, the "what you get" card)
   uses the same band so the rhythm holds, but a card has no crop to centre
   against and reads better pinned to the top of the copy. */
.split--card{align-items:start}
.split--card .split__copy{max-width:none}

@media (max-width:860px){
  .split{grid-template-columns:1fr;gap:clamp(1.75rem,4vw,2.5rem)}
  .split__copy,.split--flip .split__copy{order:1;max-width:none;justify-self:stretch}
  .split__media,.split--flip .split__media{order:2}
  /* One column, so the frame keeps its portrait crop and just loses the cap
     that kept it from towering over the copy beside it. */
  .split__media img{max-height:none}
  .split__media::before{display:none}
}

/* A card has no crop to centre against, and the plate behind a photo frame
   would show through its corners, so the --card variant drops both. */
.split--card .split__media::before{display:none}
.split--card .split__media img{max-height:none}
/* The homepage process band parks its photograph beside a long list of steps.
   Sticky only where there is room for it to travel. */
@media (min-width:861px){
  .split__media--sticky{position:sticky;top:calc(var(--header-h) + 28px)}
  .split__media--sticky img{max-height:none}
}

/* The card surface the builders were repeating inline in four places. */
/* max-width guards the narrowest phones: a panel is often a grid item, and a
   grid item defaults to min-width:auto, so it can push past its own track.
   overflow-x:hidden on <html> used to hide that; it no longer does, because
   that rule was silently killing the sticky header. */
.panel{
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  padding:clamp(1.35rem,2.6vw,1.85rem);
  box-shadow:var(--shadow);
  max-width:100%;
}
.band-ink .panel{background:var(--dark-2);border-color:var(--line-dark)}
.band-ink .panel p{color:var(--on-dark-mut)}

/* The pricing "what moves your number" block. One measure for the label, the
   list and the closing line, centred on the same axis as the section head
   above them. Previously the prose was 52rem-centred and the list ran the
   full 1264px container, which is the misalignment the client flagged. */
.movers{
  max-width:52rem;margin-inline:auto;
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow);
  padding:clamp(1.35rem,3vw,2.1rem) clamp(1.35rem,3vw,2.25rem);
}
.band-paper .movers,.band-paper2 .movers{box-shadow:var(--shadow-lg)}
.band-ink .movers{background:var(--dark-2);border-color:var(--line-dark);box-shadow:none}
.movers__label{margin-bottom:1rem}
/* The list is already inside a bordered panel, so it drops its own outer
   rules and keeps only the hairlines between rows. */
.movers .deflist{border-top:0}
.movers .deflist li:last-child{border-bottom:0;padding-bottom:0}
.movers__note{
  max-width:52rem;margin-inline:auto;
  margin-top:clamp(1.5rem,2.6vw,2rem);
}

/* The wider of the two column gaps, as a class rather than an inline style. */
.gap-wide{gap:clamp(2rem,5vw,4rem)}

/* --- 20c. Editorial layouts for copy-only bands ---------------------------
   A centred head over three bare paragraphs was the site's default for any
   band without a photograph, and on the city pages that meant two or three
   walls of text in a row. These give a copy band a second element to play
   against without inventing a second photograph for every one of them. */

/* Prose beside a sticky aside (a pull quote or a facts card). */
.aside-split{
  display:grid;
  grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);
  gap:clamp(2rem,5vw,4.5rem);
  align-items:start;
}
.aside-split__copy{max-width:38rem}
.aside-split__side{position:sticky;top:calc(var(--header-h) + 28px)}
/* Alternates with the photo bands: --flip puts the aside on the left. */
.aside-split--flip .aside-split__copy{order:2;justify-self:end}
.aside-split--flip .aside-split__side{order:1}
@media (max-width:860px){
  .aside-split{grid-template-columns:1fr;gap:1.75rem}
  .aside-split__copy,.aside-split--flip .aside-split__copy{order:1;max-width:none;justify-self:stretch}
  .aside-split__side,.aside-split--flip .aside-split__side{order:2;position:static}
}

/* The pull quote. A sentence lifted from the copy beside it, set large. */
.pull{
  position:relative;
  padding:clamp(1.5rem,3vw,2.25rem) clamp(1.5rem,3vw,2.25rem) clamp(1.5rem,3vw,2.25rem) clamp(1.85rem,3.4vw,2.6rem);
  border-radius:var(--radius-lg);
  background:var(--paper);
  border:1px solid var(--line);
  box-shadow:var(--shadow);
}
.pull::before{
  content:"";position:absolute;left:0;top:clamp(1.5rem,3vw,2.25rem);bottom:clamp(1.5rem,3vw,2.25rem);
  width:4px;border-radius:2px;background:var(--gold);
}
.pull__mark{
  display:block;
  font-family:var(--display);font-weight:700;
  font-size:3.25rem;line-height:.6;letter-spacing:-.04em;
  color:var(--gold);margin-bottom:.9rem;
}
.pull__text{
  font-family:var(--display);font-weight:600;
  font-size:clamp(1.2rem,2.1vw,1.5rem);line-height:1.32;letter-spacing:-.022em;
  color:var(--ink);text-wrap:pretty;margin:0;
}
.pull__cite{
  display:block;margin-top:1.1rem;
  font-size:.6875rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--gold-ink);
}
.band-ink .pull{background:var(--dark-2);border-color:var(--line-dark);box-shadow:none}
.band-ink .pull__text{color:var(--on-dark)}
.band-ink .pull__cite{color:var(--gold)}
.band-paper .pull,.band-paper2 .pull{box-shadow:var(--shadow-lg)}

/* The facts card. A micro label over term / value rows. */
.facts-card{
  border-radius:var(--radius-lg);
  background:var(--paper);
  border:1px solid var(--line);
  box-shadow:var(--shadow);
  padding:clamp(1.35rem,2.6vw,1.85rem);
}
.band-paper .facts-card,.band-paper2 .facts-card{box-shadow:var(--shadow-lg)}
.band-ink .facts-card{background:var(--dark-2);border-color:var(--line-dark);box-shadow:none}
.facts-card__label{margin-bottom:.9rem}
.facts-card dl{margin:0}
.facts-card__row{
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:1rem;align-items:baseline;
  padding-block:.8rem;border-top:1px solid var(--line);
}
.facts-card__row:first-of-type{border-top:0;padding-top:0}
.facts-card__row:last-of-type{padding-bottom:0}
.band-ink .facts-card__row{border-color:var(--line-dark)}
.facts-card__note{
  margin:1.1rem 0 0;padding-top:1.1rem;border-top:1px solid var(--line);
  font-size:.875rem;line-height:1.6;color:var(--muted);
}
.band-ink .facts-card__note{border-color:var(--line-dark);color:var(--on-dark-mut)}
.facts-card dt{font-size:.9375rem;color:var(--muted);margin:0}
.facts-card dd{
  margin:0;text-align:right;
  font-family:var(--display);font-weight:700;font-size:1.0625rem;letter-spacing:-.02em;
  color:var(--ink);font-variant-numeric:tabular-nums;
}
.band-ink .facts-card dt{color:var(--on-dark-mut)}
.band-ink .facts-card dd{color:var(--gold)}

/* Statement layout: the opening paragraph set large on one side, the rest
   of the copy on the other. The band's one big idea, then the detail. */
.statement{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.1fr);
  gap:clamp(2rem,5vw,4.5rem);
  align-items:start;
}
.statement__lead{
  font-family:var(--display);font-weight:600;
  font-size:clamp(1.25rem,2.3vw,1.7rem);line-height:1.32;letter-spacing:-.024em;
  color:var(--ink);text-wrap:pretty;
  padding-top:.35rem;
  border-top:4px solid var(--gold);
  padding-top:1.25rem;
  position:sticky;top:calc(var(--header-h) + 28px);
}
.band-ink .statement__lead{color:var(--on-dark)}
.statement__body{max-width:38rem}
@media (max-width:860px){
  .statement{grid-template-columns:1fr;gap:1.5rem}
  .statement__lead{position:static}
  .statement__body{max-width:none}
}

/* Numbered point cards: a strong lead-in becomes the card title. */
.kcard{
  position:relative;
  padding:1.6rem 1.5rem 1.75rem;
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow);
}
.kcard__n{
  display:inline-flex;align-items:center;justify-content:center;
  width:42px;height:42px;border-radius:10px;margin-bottom:1.1rem;
  background:var(--tint);border:1px solid var(--gold-soft);
  color:var(--gold-ink);
  font-family:var(--display);font-weight:700;font-size:.9375rem;line-height:1;
  font-variant-numeric:tabular-nums;
}
.kcard h3{font-size:1.125rem;margin-bottom:.55rem;color:var(--ink)}
.kcard p{font-size:.9375rem;line-height:1.62;color:var(--body);margin:0}
.band-ink .kcard{background:var(--dark-2);border-color:var(--line-dark);box-shadow:none}
.band-ink .kcard__n{background:rgba(212,157,17,.16);border-color:rgba(212,157,17,.4);color:var(--gold)}
.band-ink .kcard h3{color:var(--on-dark)}
.band-ink .kcard p{color:var(--on-dark-mut)}

/* A single centred line under a grid, with a pin. Replaces the bare
   left-aligned paragraph that used to trail the county list. */
.pin-note{
  display:flex;justify-content:center;align-items:center;gap:.6rem;flex-wrap:wrap;
  margin-top:clamp(2rem,3.5vw,2.75rem);
  text-align:center;color:var(--body);
}
.pin-note svg{width:18px;height:18px;color:var(--gold-ink);flex:none}
.pin-note a{color:var(--gold-ink);font-weight:500}
.pin-note__chip{
  display:inline-flex;align-items:center;gap:.6rem;
  padding:.7rem 1.2rem;border-radius:999px;
  background:var(--paper);border:1px solid var(--line);box-shadow:var(--shadow);
}

/* Stat tiles in a band that also carries copy. */
.statgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(.75rem,1.4vw,1rem)}
@media (max-width:420px){ .statgrid{grid-template-columns:1fr} }

/* Centred closers under FAQ lists and the like. */
.closer{text-align:center;margin-top:clamp(1.75rem,3vw,2.5rem)}
.closer .tlink{display:inline-flex}

/* --- 20d. The closing contact band ---------------------------------------
   Sits on every page between the CTA photo band and the footer. Light, so the
   run reads dark photo -> light form -> dark footer rather than three dark
   blocks stacked on each other.

   The form is the SAME handler as the hero card and the contact page
   (form[data-demo-form] in site.js, which finds its own [data-form-note]), so
   three forms can coexist on a page without a second code path. */
.contactus__grid{
  display:grid;
  grid-template-columns:minmax(0,1.2fr) minmax(0,.8fr);
  gap:clamp(1.25rem,2.6vw,2rem);
  align-items:start;
}
@media (max-width:860px){ .contactus__grid{grid-template-columns:minmax(0,1fr)} }

.contactus__form h3{font-size:1.125rem;margin-bottom:.35rem}
.contactus__sub{font-size:.9375rem;color:var(--muted);margin-bottom:1.5rem}
.contactus__row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 1rem}
@media (max-width:460px){ .contactus__row{grid-template-columns:1fr} }
.contactus__go{width:100%;justify-content:center;margin-top:.35rem}
.contactus__note{font-size:.8125rem;color:var(--gold-ink);margin-top:.9rem}
.contactus__micro{font-size:.8125rem;color:var(--muted);margin-top:.9rem;margin-bottom:0}

/* One contact method: icon, label, value. */
.cmethod{
  display:grid;grid-template-columns:auto minmax(0,1fr);gap:.9rem;align-items:start;
  padding-block:1.1rem;border-top:1px solid var(--line);
}
.cmethod:first-child{border-top:0;padding-top:0}
.cmethod:last-child{padding-bottom:0}
.band-ink .cmethod{border-color:var(--line-dark)}
.cmethod__ico{
  width:38px;height:38px;flex:none;
  border-radius:10px;
  background:var(--tint);border:1px solid var(--gold-soft);
  color:var(--gold-ink);
  display:grid;place-items:center;
}
.cmethod__ico svg{width:18px;height:18px}
.band-ink .cmethod__ico{background:rgba(212,157,17,.16);border-color:rgba(212,157,17,.4);color:var(--gold)}
.cmethod__l{
  display:block;margin-bottom:.2rem;
  font-size:.6875rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--gold-ink);
}
.band-ink .cmethod__l{color:var(--gold)}
.cmethod__v{
  display:block;margin:0;
  font-family:var(--display);font-weight:700;
  font-size:1.0625rem;line-height:1.3;letter-spacing:-.02em;
  color:var(--ink);text-decoration:none;
  overflow-wrap:anywhere;
}
a.cmethod__v:hover{color:var(--gold-ink)}
.band-ink .cmethod__v{color:var(--on-dark)}
.cmethod__d{margin:.35rem 0 0;font-size:.875rem;line-height:1.55;color:var(--muted)}
.band-ink .cmethod__d{color:var(--on-dark-mut)}
/* The phone is the one people actually use: set it a size up. */
.cmethod--call .cmethod__v{font-size:clamp(1.25rem,2.2vw,1.5rem)}

/* The form-free variant (contact page, thank-you page): the same methods as a
   row of cards rather than a sidebar, so those pages still close on a contact
   block without carrying a second copy of the form. */
.cmethods-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(1rem,2vw,1.5rem)}
.cmethods-row .cmethod{
  border-top:0;padding:1.5rem 1.4rem;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius-lg);
  box-shadow:var(--shadow);
  grid-template-columns:1fr;gap:1rem;align-content:start;
}
.band-paper .cmethods-row .cmethod,.band-paper2 .cmethods-row .cmethod{box-shadow:var(--shadow-lg)}
.band-ink .cmethods-row .cmethod{background:var(--dark-2);border-color:var(--line-dark);box-shadow:none}
@media (max-width:760px){ .cmethods-row{grid-template-columns:1fr;max-width:26rem;margin-inline:auto} }

/* --- 21. Utilities ------------------------------------------------------- */

.stack-sm > * + *{margin-top:.75rem}
.stack    > * + *{margin-top:1.5rem}
.stack-lg > * + *{margin-top:2.5rem}
.center{text-align:center}

/* Measure-limited blocks are CENTRED in their container by default.
   Every one of these sits under a centred .sec-head, and a 40rem column
   pinned to the left edge under a centred head is exactly the "not centered"
   the client is seeing: the heading is on the page axis, the paragraph under
   it is not. Inside a grid column the auto margins are a no-op, because the
   column is already narrower than the max-width. Use .mw-prose--left where a
   flush-left column is genuinely wanted. */
.mw-prose{max-width:40rem;margin-inline:auto}
.mw-narrow{max-width:34rem;margin-inline:auto}
.mw-prose--left,.mw-narrow--left{margin-inline:0}
/* Same rule for the inline max-width blocks the builders emit. */
.mw-wide{max-width:52rem;margin-inline:auto}
.hide{display:none !important}

/* --- 22. Motion ---------------------------------------------------------- */
/* Content is VISIBLE by default. site.js stamps .js on <html> only once it is
   actually running, so a JS failure, a crawler, or a blocked script all still
   see every word. */

.rv{opacity:1;transform:none}
.js .rv{opacity:0;transform:translateY(14px);transition:opacity .5s ease,transform .5s ease}
.js .rv.is-vis{opacity:1;transform:none}

/* Vestigial hooks from the old GSAP/Lenis stack. The libraries are gone; these
   stay as inert no-ops so any leftover markup renders correctly. */
.lenis,.line-mask,.line-in{display:block}
.line-mask{overflow:visible}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
  .rv,.js .rv{opacity:1 !important;transform:none !important}
  .marquee__track{animation:none !important;width:auto;flex-wrap:wrap;justify-content:center}
  .marquee__item{white-space:normal}
  .hero__cue span{animation:none;transform:none}
}

/* --- 23. Print ----------------------------------------------------------- */

@media print{
  .hdr,.drawer,.hero__bg,.hero__cue,.facts,.socials,.hero__formcol{display:none !important}
  .hero,.ctaband,.band-ink,.ftr{background:#fff !important;color:#000 !important}
  .hero__title,.hero__lede,.ctaband h2,.band-ink h2,.band-ink p{color:#000 !important}
  .ctaband img{display:none !important}
}

/* === Google review cards (reviews page) =====================================
   Real reviews, scraped verbatim from the Business Profile. Deliberately given
   a Google-ish card look so they read as genuine third-party reviews rather
   than testimonials we wrote. No review schema: self-reported aggregate markup
   on your own site is a policy risk. */
.grevs{display:grid;gap:1.25rem;grid-template-columns:1fr}
@media (min-width:700px){ .grevs{grid-template-columns:repeat(2,1fr)} }
@media (min-width:1100px){ .grevs{grid-template-columns:repeat(3,1fr)} }
.grev{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:1.5rem;display:flex;flex-direction:column;gap:.75rem;box-shadow:var(--shadow);
}
.grev__head{display:flex;align-items:center;gap:.75rem}
.grev__ava{
  inline-size:40px;block-size:40px;border-radius:50%;flex:0 0 auto;
  display:grid;place-items:center;background:var(--tint);color:var(--gold-ink);
  font-weight:700;font-size:1.0625rem;line-height:1;
}
.grev__meta{display:flex;flex-direction:column;min-inline-size:0}
.grev__name{font-size:.9375rem;font-weight:600;color:var(--ink);line-height:1.3}
.grev__sub{font-size:.75rem;color:var(--muted)}
.grev__stars{display:flex;gap:2px}
.grev__stars svg{display:block}
.grev__text{font-size:.9375rem;line-height:1.65;color:var(--body)}
.grev__text p{margin:0 0 .75rem}
.grev__text p:last-child{margin:0}

/* ---------------------------------------------------------------------------
   Sticky mobile call bar.

   Phone leads convert far better than form leads in this trade, and these
   pages are long: the homepage is about 17,700px tall at 375px. The header
   number is therefore off screen for nearly all of a visitor's scroll, which
   left calling as a scroll-back-to-top action. This puts call and book one
   thumb away for the whole page.

   Sits on --plate so it reads as the same chrome as the header and footer,
   which are the other two elements on that ground. Under 900px only: that is
   the same breakpoint where the desktop nav becomes the burger, so the bar
   appears exactly when the header's own phone link disappears.

   Both buttons clear 48px, above the 44px minimum, and the bar respects
   safe-area-inset-bottom so it is not swallowed by the iOS home indicator.
   --------------------------------------------------------------------------- */
.callbar{
  position:fixed;left:0;right:0;bottom:0;z-index:70;
  display:none;
  gap:.5rem;
  padding:.5rem .75rem calc(.5rem + env(safe-area-inset-bottom,0px));
  background:var(--plate);
  border-top:1px solid var(--plate-line-2);
  box-shadow:0 -12px 28px -20px rgba(0,0,0,.9);
  transform:translateY(110%);
  transition:transform .28s var(--ease);
}
/* Only ever a mobile/tablet element. Above this width the header keeps its
   own phone link and estimate button on screen, so a second bar is noise. */
@media (max-width:899px){
  .callbar{display:flex}
  .callbar.is-on{transform:translateY(0)}
  /* Keep the last of the footer clear of the bar so nothing is covered at the
     very bottom of the page. */
  body{padding-bottom:0}
  body.has-callbar{padding-bottom:calc(64px + env(safe-area-inset-bottom,0px))}
}
/* The call button carries a 10-digit number and Book Visit carries two short
   words, so an even 1:1 split wrapped the number onto two lines at 360-375px.
   Weighting the split lets the number stay on one line at every width. */
.callbar__btn{
  flex:1 1 0;min-height:48px;
  white-space:nowrap;
  display:inline-flex;align-items:center;justify-content:center;gap:.5rem;
  border-radius:var(--radius);
  font-family:var(--sans);font-size:.9375rem;font-weight:600;line-height:1;
  text-decoration:none;text-align:center;
  padding:0 .875rem;
  transition:background .2s var(--ease),border-color .2s var(--ease),color .2s var(--ease);
}
.callbar__btn svg{width:18px;height:18px;flex:none}
/* Call is the primary action of the two: gold fill, dark ink, 7.44:1. */
.callbar__btn--call{
  flex:1.55 1 0;
  background:var(--gold);border:1px solid var(--gold);color:var(--ink);
}
.callbar__btn--call:hover,.callbar__btn--call:active{
  background:var(--gold-hover);border-color:var(--gold-hover);
}
/* Book is deliberately subordinate: outline on the dark ground, 15.2:1. */
.callbar__btn--book{
  background:transparent;border:1px solid var(--plate-line-2);color:var(--on-dark);
}
.callbar__btn--book:hover,.callbar__btn--book:active{
  border-color:var(--gold);color:var(--gold);
}
.callbar :focus-visible{outline:2px solid var(--gold);outline-offset:2px}

/* Small phones (iPhone SE and similar): trim the gutters and the label so the
   full number still fits on one line rather than shrinking the tap target. */
@media (max-width:389px){
  .callbar{padding-left:.5rem;padding-right:.5rem;gap:.4rem}
  .callbar__btn{font-size:.875rem;padding:0 .625rem;gap:.375rem}
  .callbar__btn svg{width:16px;height:16px}
}

/* While the drawer is open it owns the screen, so the bar gets out of the way
   rather than floating over a modal. */
.drawer.is-open ~ .callbar,
body.nav-open .callbar{transform:translateY(110%)}

@media (prefers-reduced-motion:reduce){
  .callbar{transition:none}
}

/* ==========================================================================
   PRODUCT CATALOGUE
   products.html, the six collection-*.html pages, and the 47 product-*.html
   pages, all generated by build_products.py.

   Almost everything here reuses a component that already existed: the cards
   are .pcard (the homepage project card), the spec tables are .ptable inside
   .tscroll, the room scenes are .figp inside .photostrip, and the collection
   filter is .filters + [data-cat], which site.js already drives. What follows
   is only the handful of things the catalogue genuinely adds.
   ========================================================================== */

/* Visually hidden, still read aloud. Used on the spec tables' <caption>, which
   gives each table an accessible name without printing a redundant heading
   directly under the <h2> that already says the same thing. */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}

/* --- breadcrumb ----------------------------------------------------------
   A product is four levels deep (Home / Products / Collection / Colour), which
   is the deepest anything on this site goes, so unlike the rest of the site
   these pages carry a visible trail as well as the JSON-LD one. */
.crumbs{
  background:var(--paper-2);
  border-bottom:1px solid var(--line);
  font-size:.8125rem;
  padding-block:.85rem;
}
.crumbs .wrap{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}
.crumbs a{color:var(--muted);text-decoration:none;border-bottom:1px solid transparent}
.crumbs a:hover{color:var(--gold-ink);border-bottom-color:var(--gold-soft)}
.crumbs [aria-current="page"]{color:var(--ink);font-weight:600}
.crumbs__sep{color:var(--line-2);user-select:none}

/* --- price ---------------------------------------------------------------
   The unit is part of the component, never optional. A bare "$3.89" beside a
   floor reads as the price of the floor; this is the price of the material per
   square foot, and the two differ by orders of magnitude. See the long note at
   the top of build_products.py. */
.price{display:inline-flex;align-items:baseline;gap:.4rem}
.price__n{
  font-family:var(--display);font-weight:700;
  font-size:1.5rem;letter-spacing:-.03em;color:var(--gold-ink);
  font-variant-numeric:tabular-nums;line-height:1;
}
.price__u{font-size:.875rem;color:var(--muted);font-weight:500}
.price--lg .price__n{font-size:clamp(1.9rem,4.2vw,2.6rem)}
.price--lg .price__u{font-size:1rem}

/* The disclosure that rides under every price on the site. Small, but it is
   the sentence that stops a material price being read as a job price. */
.matnote{
  margin:.85rem 0 0;
  font-size:.8125rem;line-height:1.55;color:var(--muted);
  max-width:34rem;
}
.band-ink .matnote{color:var(--on-dark-mut)}

/* --- collection hero -----------------------------------------------------
   Not the photographic .hero: a collection leads with the product itself on a
   light ground, because a full-bleed dark hero would fight the swatch that is
   the whole point of the page. */
.collhero{background:var(--paper-2);border-bottom:1px solid var(--line)}
.collhero__in{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:clamp(1.5rem,4vw,3.5rem);align-items:center;
  padding-block:clamp(2.25rem,5vw,4rem);
}
.collhero__copy{min-width:0}
.collhero__copy .lede{margin-top:.9rem;max-width:40rem}
.collhero__price{margin-top:1.4rem}
.collhero__media{margin:0;min-width:0}
.collhero__media img{
  width:100%;aspect-ratio:4/3;object-fit:cover;
  border-radius:var(--radius-img);border:1px solid var(--line);
  background:var(--sand);
}
@media (max-width:860px){
  .collhero__in{grid-template-columns:1fr}
  .collhero__media{order:-1}
  .collhero__media img{aspect-ratio:16/10}
}

/* --- product detail ------------------------------------------------------ */
.pdp{
  display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);
  gap:clamp(1.5rem,4vw,3.25rem);align-items:start;
}
.pdp__media{min-width:0}
.pdp__main{margin:0}
.pdp__main img{
  width:100%;aspect-ratio:4/3;object-fit:cover;
  border-radius:var(--radius-img);border:1px solid var(--line);
  background:var(--sand);
}
.pdp__buy{min-width:0}
.pdp__coll{color:inherit;text-decoration:none;border-bottom:1px solid var(--gold-soft)}
.pdp__coll:hover{color:var(--gold-ink)}
.pdp__title{margin:.35rem 0 .5rem}
.pdp__sku{
  font-size:.75rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted);margin:0 0 1.25rem;
}
.pdp__price{margin:0}
.pdp__buy .btn-row{margin-top:1.75rem}
@media (max-width:860px){
  .pdp{grid-template-columns:1fr}
}

/* --- grids ---------------------------------------------------------------
   The catalogue grids hold 47 cards, so they go narrower than the site's
   general .g4 before collapsing: a product card stays readable at a quarter
   width because it is a swatch plus two short lines. */
@media (min-width:961px){
  .pgrid{grid-template-columns:repeat(4,minmax(0,1fr))}
}
@media (max-width:960px){
  .pgrid{grid-template-columns:repeat(3,minmax(0,1fr))}
}
@media (max-width:720px){
  .pgrid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:420px){
  .pgrid{grid-template-columns:1fr;max-width:22rem;margin-inline:auto}
}

/* The filter hides a card by hiding its wrapper, since each card in the
   filterable grid is wrapped in the [data-cat] element site.js toggles. */
.pgrid [data-cat].hide{display:none}

/* A swatch is a flat texture, not a room: 3/4 portrait crops it to a strip
   that reads as nothing. The catalogue cards override .pcard's ratio. */
.pgrid .pcard__media,
.grid .pcard__media{aspect-ratio:4/3}

/* --- spec table ----------------------------------------------------------
   Two columns, label left and value right, which is how a spec sheet is read.
   .ptable already supplies the rules, the hover and the dark-band handling. */
.specs td{vertical-align:top}
.specs tbody td:first-child{width:42%;white-space:normal}
.specs tbody td:last-child{text-align:right;color:var(--body)}
@media (max-width:560px){
  .specs tbody td:first-child{width:50%}
}

/* --- product description --------------------------------------------------
   The supplier copy is long and dense. build_products.describe() splits it
   into a lead sentence, body paragraphs and a caveat; this gives each of those
   a shape so the block reads as an article rather than a wall.

   The measure is the important part: the old markup put ~2,450 characters
   through .lede inside a 760px wrap, which is roughly 95 characters a line.
   Anything past about 75 loses the reader on the carriage return. */
.pdesc{max-width:34rem;margin-inline:auto}
.pdesc__lead{
  font-family:var(--display);font-weight:700;
  font-size:clamp(1.15rem,2.4vw,1.45rem);line-height:1.34;
  letter-spacing:-.02em;color:var(--ink);
  margin:0 0 1.4rem;text-wrap:pretty;
}
/* A gold rule under the lead separates "what this colour is" from the shared
   collection copy underneath it. */
.pdesc__lead::after{
  content:"";display:block;width:52px;height:3px;border-radius:2px;
  background:var(--gold);margin-top:1.1rem;
}
.pdesc p{
  font-size:1rem;line-height:1.7;color:var(--body);
  margin:0 0 1.05rem;text-wrap:pretty;
}
.pdesc p:last-child{margin-bottom:0}
.pdesc__note{
  margin-top:1.75rem;padding:1rem 1.15rem;
  background:var(--paper-2);border-left:3px solid var(--gold-soft);
  border-radius:0 var(--radius-sm,6px) var(--radius-sm,6px) 0;
}
.pdesc__note p{
  font-size:.875rem;line-height:1.6;color:var(--muted);margin:0;
}
.pdesc__note b{
  display:block;font-size:.6875rem;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--gold-ink);margin-bottom:.4rem;
}
