/* Archivo, self-hosted. Variable weight 400-800 in a single file, so all four
   weights the site uses cost one download. Previously loaded from Google Fonts,
   which was render-blocking: a cross-origin round trip before first paint plus
   a second to fonts.gstatic.com. Self-hosting also lets the CSP drop to 'self'.
   Two axes: weight 100-900 and WIDTH 62-125%. The width axis is the point --
   headlines run expanded and body runs normal, so the page gets real
   typographic range out of one file and one download.
   SIL Open Font License 1.1 -- see fonts/OFL.txt. */
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url("/fonts/archivo-latin.woff2") format("woff2");
  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: "Archivo";
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url("/fonts/archivo-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Lamberth Waterproof Systems */

:root {
  /* Derived from the logo (img/brand/logo.svg): navy #143478, cyan #07A6F2.
     The site and the mark are the same palette again -- the editorial redesign
     had drifted onto an unrelated warm scheme.

     Roles are not interchangeable. Every pair below is verified:
       navy on white          11.72   heading, link-adjacent
       white on navy           11.72   solid button, dark ground
       white on navy-deep      16.57   hero, stats, contact, footer
       cyan on navy-deep        6.11   eyebrow / accent on dark ONLY
       cyan-text on white       5.45   links and eyebrows on light
       navy on gold             5.43   PRIMARY CTA -- gold never takes white text
       navy on gold-hi          6.08   CTA hover (lighter, not darker)
       body on white            7.98
     Raw cyan is 2.71 on white and gold is 2.16 on white: neither may ever be
     text on a light ground, and gold may never carry white text. */
  --navy-deep: #0A1D42;   /* dark grounds */
  --navy:      #143478;   /* logo navy -- headings, solid buttons */
  --navy-2:    #2B3A52;   /* lead paragraphs */
  --cyan:      #07A6F2;   /* logo cyan: graphics and rules on dark only */
  --cyan-dk:   #3FBCF7;   /* cyan as TEXT on dark: clears AA on #1E3763 */
  --cyan-text: #095A86;   /* cyan darkened for light grounds. Sized against the
                             WORST case, which is text sitting on a grout line of
                             the tile grid, not the plain band: the line is navy at
                             8% and drags the local background down. */
  --gold:      #E8A33D;   /* warm CTA -- reads against cool jobsite photography */
  --gold-hi:   #EEAF52;   /* CTA hover */
  --on-gold:   #143478;   /* the only foreground gold is allowed to carry */
  --body:      #46525F;
  --bg:        #FFFFFF;
  --bg-alt:    #F2F6FA;
  --line:      #D8E0E8;
  --wrap:      1180px;
  --callbar-h: 60px;
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; scroll-padding-top: 120px; }

body {
  margin: 0;
  background: #0A1D42;
  color: var(--body);
  font-family: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 17px;
  line-height: 1.68;
}

h1, h2, h3, h4 {
  color: var(--navy);
  margin: 0 0 .5em;
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.025em;
}
h1, h2 { font-stretch: 116%; }
h3     { font-stretch: 108%; }
h1 { font-size: clamp(2.2rem, 6.5vw, 5rem); font-weight: 800; }
h2 { font-size: clamp(2rem, 4.2vw, 3.2rem); }
h3 { font-size: 1.28rem; letter-spacing: -0.015em; }
p { margin: 0 0 1.15em; }
a { color: var(--cyan-text); }
img { display: block; max-width: 100%; height: auto; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 24px; }
.narrow { max-width: 780px; }
.measure { max-width: 64ch; }

section { padding: 108px 0; }
.eyebrow {
  font-size: .76rem; letter-spacing: .2em; text-transform: uppercase;
  font-weight: 700; font-stretch: 88%; color: var(--cyan-text); margin-bottom: 18px;
}
.lead { font-size: 1.24rem; line-height: 1.6; color: var(--navy-2); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; gap: .6rem;
  padding: 17px 34px; border: 0; cursor: pointer;
  font: inherit; font-weight: 700; font-size: 1.02rem; letter-spacing: -0.01em;
  text-decoration: none; transition: background .18s ease, transform .18s ease;
}
.btn svg { width: 19px; height: 19px; }
.btn-rust { background: var(--gold); color: var(--on-gold); }
.btn-rust:hover { background: var(--gold-hi); transform: translateY(-2px); }
.btn-ink { background: var(--navy); color: #fff; }
.btn-ink:hover { background: var(--navy-deep); transform: translateY(-2px); }
.btn-line { background: transparent; color: #fff; box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.45); }
.btn-line:hover { background: rgba(255,255,255,.12); }

/* ---------- Header ---------- */
header.site { position: absolute; top: 0; left: 0; right: 0; z-index: 60; padding: 26px 0; }
header.site .wrap { display: flex; align-items: center; justify-content: space-between; gap: 24px; }
.brand { text-decoration: none; display: flex; align-items: center; gap: 12px; }
.brand-mark {
  width: 42px; height: 42px; flex: 0 0 42px; border-radius: 10px;
  background: #fff; padding: 4px; box-sizing: border-box;
}
.brand-text { display: block; }
.brand-text strong {
  display: block; color: #fff; font-size: 1.12rem; font-weight: 800;
  letter-spacing: -.02em; line-height: 1.1;
}
.brand-text span {
  display: block; color: rgba(255,255,255,.62);
  font-size: .68rem; letter-spacing: .19em; text-transform: uppercase; margin-top: 3px;
}
nav.main { display: flex; align-items: center; gap: 30px; }
nav.main a { color: rgba(255,255,255,.9); text-decoration: none; font-weight: 600; font-size: .95rem; }
nav.main a:hover { color: #fff; }
/* nav.main a is (0,1,2) and would otherwise beat .btn-rust (0,1,0), painting
   white text onto the gold call button at 2.16:1. Gold only ever carries navy. */
nav.main a.btn-rust { color: var(--on-gold); }
nav.main a.btn-rust:hover { color: var(--on-gold); }
nav.main .btn { padding: 12px 22px; font-size: .93rem; }
.navtoggle { display: none; background: none; border: 0; padding: 8px; cursor: pointer; }
.navtoggle span { display: block; width: 26px; height: 2px; background: #fff; margin: 5px 0; }

header.site.stuck {
  position: fixed; background: var(--navy-deep); padding: 14px 0;
  animation: drop .3s ease;
}
@keyframes drop { from { transform: translateY(-100%); } to { transform: none; } }


/* ---------- Header on a light ground ----------
   The page now opens white, so the header reads dark until it sticks, at which
   point it lands on navy and flips to white. The logo also stops needing its
   white chip up here: navy-and-cyan on white is the mark's native state. */
header.site .brand-text strong { color: var(--navy); }
header.site .brand-text span   { color: var(--muted-brand, #6B7A90); }
header.site .brand-mark        { background: transparent; padding: 0; }
nav.main a                     { color: var(--navy); }
nav.main a:hover               { color: var(--cyan-text); }
nav.main a[aria-current="page"]{ color: var(--cyan-text); }
.navtoggle span                { background: var(--navy); }

/* The mobile dropdown paints itself navy, so its links must flip regardless of
   whether the header is stuck. Without this the menu is navy-on-navy at 1.4:1,
   which is the state a visitor sees every time they open it on a phone. */
nav.main.open a                       { color: #fff; }
nav.main.open a:hover,
nav.main.open a[aria-current="page"]  { color: var(--cyan-dk); }
nav.main.open .nav-tel                { color: rgba(255,255,255,.86); }
nav.main.open .btn-rust               { color: var(--on-gold); }

header.site.stuck .brand-text strong,
header.site.stuck nav.main a          { color: #fff; }
header.site.stuck .brand-text span    { color: rgba(255,255,255,.62); }
header.site.stuck nav.main a:hover,
header.site.stuck nav.main a[aria-current="page"] { color: var(--cyan); }
header.site.stuck .navtoggle span     { background: #fff; }
header.site.stuck .brand-mark         { background: #fff; padding: 4px; }

/* The number is available, not shouted. The form is the call to action. */
.nav-tel {
  font-size: .9rem; font-weight: 600; letter-spacing: -.01em;
  white-space: nowrap; text-decoration: none;
}

/* ---------- Type-led opening ----------
   No photograph. The argument is the first thing on the page, at a size that
   makes it the only thing. Photographs arrive later, as evidence. */
.hero-type { padding: 190px 0 104px; }
.hero-type h1 {
  font-stretch: 122%;
  font-size: clamp(2.5rem, 7vw, 5.9rem);
  line-height: .96; letter-spacing: -.042em; font-weight: 800;
  margin: 0 0 .38em; max-width: 16ch;
}
.hero-type h1 em { font-style: normal; color: var(--cyan-text); }
.hero-type .lead { font-size: clamp(1.1rem, 1.9vw, 1.44rem); max-width: 46ch; }
.hero-type .hero-cta { margin-top: 38px; }
.hero-type .eyebrow { margin-bottom: 26px; }

/* ---------- Two-field opening ----------
   Type holds the left, one photograph holds the right. The panel starts below
   the header rather than bleeding to the top edge: the nav sits over this area
   and navy links on a photograph is a contrast problem with no fix. It bleeds
   right and bottom, so it reads as a field in a composition rather than a card. */
.hero-split {
  display: grid; align-items: stretch; padding: 0;
  grid-template-columns: minmax(0, 1fr) clamp(300px, 41%, 640px);
  min-height: min(94vh, 960px);
}
.hero-copy {
  display: flex; flex-direction: column; justify-content: center;
  padding: 150px 52px 92px max(24px, calc((100vw - var(--wrap)) / 2 + 24px));
}
/* The <br>s set the line breaks; a max-width here would re-wrap them and
   push the call to action below the fold. Sized so the longest line fits
   the left column at 1280 and up. */
.hero-split h1 { max-width: none; font-size: clamp(2.1rem, 4.1vw, 4.3rem); }
.hero-split .lead { max-width: 40ch; margin-bottom: 0; }
.hero-shot { position: relative; margin-top: 108px; overflow: hidden; background: #E3ECF5; }
.hero-shot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

.hero-proof {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 26px;
  /* No rule above these. The tile field already gives the ground horizontal
     structure, and a hairline in the same hue as the grout but thinner and
     darker reads as a grout line that went wrong rather than as a divider.
     Spacing separates them instead. */
  margin: 46px 0 0; padding-top: 0;
  max-width: 40rem;
}
.hero-proof b {
  display: block; font-size: clamp(1.5rem, 2.2vw, 2rem); font-weight: 800;
  font-stretch: 118%; letter-spacing: -.035em; line-height: 1; color: var(--navy);
}
.hero-proof span {
  display: block; margin-top: 8px; font-size: .82rem; line-height: 1.42; color: var(--body);
}

@media (max-width: 900px) {
  .hero-split { grid-template-columns: 1fr; min-height: 0; }
  .hero-copy { padding: 118px 24px 44px 24px; }
  .hero-split h1 { font-size: clamp(2.1rem, 8.4vw, 3.2rem); }
  .hero-shot { margin-top: 0; aspect-ratio: 4/3; }
  .hero-proof { gap: 16px; margin-top: 34px; padding-top: 0; }
  .hero-proof span { font-size: .74rem; }
}

/* ---------- Interior masthead ----------
   Deliberately not a hero. The home page opens with a statement; an interior
   page opens with a title and then gets out of the way. Measured before this
   existed: four elements spread over 730-985px, 53-57% vertical fill. */
.masthead { padding: 134px 0 26px; }
/* Deliberately subordinate to the home page headline, which runs to 4.3rem and
   122% width. This is a document title, not a statement: smaller, narrower,
   tighter, and it hands over to the content immediately. */
.masthead h1 {
  font-size: clamp(1.7rem, 2.9vw, 2.6rem); font-stretch: 106%;
  letter-spacing: -.03em; line-height: 1.04; max-width: 24ch; margin-bottom: .34em;
}
.masthead .lead { font-size: 1.08rem; max-width: 58ch; margin-bottom: 0; }
.masthead .eyebrow { margin-bottom: 14px; }
/* Interior section headings sit UNDER the page title, so they must be smaller
   than it. At landing-page scale they were 51px against a 42px h1, which
   inverts the hierarchy and is most of why these pages read as spread out. */
.masthead ~ section h2 { font-size: clamp(1.45rem, 2.3vw, 2rem); font-stretch: 110%; }
.masthead ~ section h3 { font-size: 1.14rem; }
.masthead ~ section .lead { font-size: 1.1rem; }
.masthead ~ section { padding: 76px 0; }
.masthead ~ .photos { padding: 76px 0 84px; }

@media (max-width: 900px) {
  .masthead { padding: 104px 0 22px; }
  .masthead ~ section { padding: 54px 0; }
  .masthead h1 { font-size: clamp(1.7rem, 6.4vw, 2.1rem); }
  .masthead .lead { font-size: 1rem; }
}

/* ---------- Interior section spine ----------
   These sections used class="wrap narrow", i.e. BOTH classes, so .wrap's
   `margin: 0 auto` re-centred a 780px box against the viewport instead of
   constraining a column inside the 1180px grid. Measured on /shower-pans/ at
   1440: masthead 154, prose 354, photo grid 154. Three left edges on one page,
   and a left-to-centre lurch at every section boundary.
   Label and heading now hold a fixed left column on the same 154 spine as the
   masthead and the photo strips; the argument runs in the field beside them.
   One left edge, two measures.
   .sec-body is deliberately ONE grid cell rather than one row per paragraph:
   grid rows span both columns, so a tall heading in column 1 stretches the row
   and pushes the next paragraph down by the leftover height. Inside a single
   cell the paragraphs are normal block flow and never see the grid. */
.sec-head { margin-bottom: 20px; }
.sec-body { max-width: 62ch; }
.sec-body > :last-child { margin-bottom: 0; }

@media (min-width: 901px) {
  .wrap.sec, .sec-list .faq-item {
    display: grid;
    grid-template-columns: minmax(0, 21rem) minmax(0, 1fr);
    column-gap: clamp(40px, 5vw, 76px);
    align-items: start;
  }
  .sec-head { grid-column: 1; margin-bottom: 0; }
  .sec-head h2 { margin-bottom: 0; }
  .sec-body { grid-column: 2; }
  /* Same spine per question. A Q&A is already a two-column idea.
     Each .faq-item holds exactly ONE <p>, so the answer is a single cell and
     the row-stretch problem above cannot arise. Adding a second paragraph to an
     item would put it in its own grid row and reopen the gap -- if an answer
     ever needs two, wrap them in a .sec-body div instead. */
  .sec-list .faq-item h2 { grid-column: 1; margin-bottom: 0; }
  .sec-list .faq-item p  { grid-column: 2; max-width: 62ch; }
}

/* ---------- Proof, inline ---------- */
.stats-inline {
  display: grid; gap: 30px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  border-top: 1px solid rgba(20,52,120,.16);
  padding-top: 34px;
}
.stats-inline .stat { padding: 0; }
.stats-inline .stat b {
  display: block; font-size: clamp(2.1rem, 3.6vw, 3rem); font-weight: 800;
  line-height: 1; letter-spacing: -.035em; color: var(--navy);
}
.stats-inline .stat span {
  display: block; margin-top: 9px; color: var(--body); font-size: .95rem;
}

/* ---------- Three routes into the site ---------- */
.routes {
  display: grid; gap: 2px; margin-top: 76px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  border-top: 1px solid rgba(20,52,120,.16);
}
.route {
  display: flex; flex-direction: column; gap: 10px;
  padding: 30px 26px 30px 0; text-decoration: none;
  border-bottom: 1px solid rgba(20,52,120,.16);
  transition: padding-left .2s ease;
}
.route:hover { padding-left: 12px; }
.route-k { font-size: 1.24rem; font-weight: 700; color: var(--navy); letter-spacing: -.02em; }
.route-d { font-size: .97rem; color: var(--body); flex: 1; }
.route-g { font-size: .84rem; font-weight: 700; color: var(--cyan-text); letter-spacing: .02em; }
.route-g::after { content: " \2192"; }

/* ---------- Index: the three ways in ----------
   A list, not a card grid. Full-bleed rows with the number set wide and large
   so it reads as an index in a book rather than three boxes in a row. */
.index { list-style: none; margin: 76px 0 0; padding: 0; border-top: 1px solid rgba(20,52,120,.18); }
.index li { border-bottom: 1px solid rgba(20,52,120,.18); }
.index a {
  display: grid; grid-template-columns: 5.5rem minmax(max-content, 19ch) 1fr; align-items: baseline;
  gap: 8px 34px; padding: 30px 0; text-decoration: none;
  transition: padding-left .22s cubic-bezier(.3,.7,.3,1), background-color .22s ease;
}
.index a:hover { padding-left: 16px; background: rgba(20,52,120,.035); }
.index-n {
  font-size: 2.1rem; font-weight: 800; font-stretch: 122%; letter-spacing: -.04em;
  color: var(--cyan-text); opacity: .38; line-height: 1;
}
.index-t { font-size: 1.5rem; font-weight: 700; font-stretch: 112%; letter-spacing: -.028em; color: var(--navy); }
.index-d { font-size: 1rem; color: var(--body); }
@media (max-width: 860px) {
  .index a { grid-template-columns: 3.2rem 1fr; gap: 4px 18px; padding: 24px 0; }
  .index-n { font-size: 1.5rem; }
  .index-d { grid-column: 2; }
}

/* ---------- Sequence: how a job runs ---------- */
.seq { list-style: none; margin: 0; padding: 0; counter-reset: seq; }
.seq li {
  display: grid; grid-template-columns: 6.5rem 1fr; gap: 30px;
  padding: 26px 0; border-top: 1px solid rgba(255,255,255,.16); align-items: start;
}
.seq li:last-child { border-bottom: 1px solid rgba(255,255,255,.16); }
.seq-n {
  font-size: 2.6rem; font-weight: 800; font-stretch: 122%; line-height: .9;
  letter-spacing: -.05em; color: var(--cyan-dk); opacity: .5;
}
.seq-b strong {
  display: block; font-size: 1.2rem; font-stretch: 108%; letter-spacing: -.02em;
  margin-bottom: 7px;
}
@media (max-width: 700px) {
  .seq li { grid-template-columns: 3.4rem 1fr; gap: 18px; }
  .seq-n { font-size: 1.8rem; }
}

/* ---------- Split action bar ----------
   The estimate is the action; the phone stays one thumb away beside it. */
.callbar { display: none; position: fixed; bottom: 0; left: 0; right: 0; z-index: 90; background: transparent; }
.callbar-inner { display: flex; }
.callbar .cb-form {
  flex: 1; display: flex; align-items: center; justify-content: center;
  padding: 17px 14px; background: var(--gold); color: var(--on-gold);
  font-weight: 700; font-size: 1.05rem; text-decoration: none;
}
.callbar .cb-tel {
  flex: 0 0 68px; display: flex; align-items: center; justify-content: center;
  background: var(--navy); color: #fff; text-decoration: none;
}
.callbar .cb-tel svg { width: 22px; height: 22px; }

/* ---------- Hero ---------- */
.hero {
  position: relative; min-height: 92vh; display: flex; align-items: flex-end;
  background: var(--navy-deep); overflow: hidden;
}
.hero-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .55; }
.hero::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(7,20,48,.94) 0%, rgba(7,20,48,.5) 45%, rgba(7,20,48,.72) 100%);
}
.hero .wrap { position: relative; z-index: 2; min-width: 0; padding-bottom: 84px; padding-top: 150px; }
.hero h1 { color: #fff; max-width: 15ch; margin-bottom: .35em; }
.hero .eyebrow { color: var(--cyan); }
.hero .lead { color: rgba(255,255,255,.84); max-width: 52ch; font-size: 1.3rem; }
.hero-cta { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 36px; }

/* Sub-page hero: same navy ground, no photograph, so no scrim and no 92vh. */
.hero-sub { min-height: 0; align-items: center; }
.hero-sub::after { display: none; }
.hero-sub .wrap { padding-top: 156px; padding-bottom: 76px; min-width: 0; }
.hero-sub h1 { font-size: clamp(2rem, 5vw, 3.4rem); max-width: 20ch; }
.hero-sub .lead { max-width: 56ch; }

.failures { list-style: none; padding: 0; margin: 0 0 1.4em; }
.failures li {
  padding: 16px 0 16px 26px; border-bottom: 1px solid var(--line);
  position: relative;
}
.failures li::before {
  content: ""; position: absolute; left: 0; top: 27px;
  width: 10px; height: 2px; background: var(--cyan-text);
}
.failures li:last-child { border-bottom: 0; }
.failures strong { color: var(--navy); }

/* ---------- Stat strip ---------- */
.stats { background: var(--navy-deep); color: #fff; padding: 0; }
.stats .wrap { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.stat { padding: 46px 30px 46px 0; }
.stat b {
  display: block; font-size: clamp(2.6rem, 5vw, 3.9rem); font-weight: 800;
  line-height: 1; letter-spacing: -.03em; color: #fff;
}
.stat span { display: block; margin-top: 10px; color: rgba(255,255,255,.6); font-size: .95rem; }

/* ---------- Editorial rows ---------- */
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 72px; align-items: center; margin-bottom: 108px; }
.row:last-child { margin-bottom: 0; }
.row.flip .row-media { order: 2; }
.row-media img { width: 100%; aspect-ratio: 4/3; object-fit: cover; }
.row h3 { font-size: clamp(1.6rem, 3vw, 2.15rem); margin-bottom: .45em; }

.rowlink { margin: 0; font-weight: 700; }
.rowlink a { color: var(--cyan-text); text-decoration: none; }
.rowlink a:hover { text-decoration: underline; }

/* ---------- Full-bleed photo band ---------- */
.band { position: relative; min-height: 60vh; display: flex; align-items: center; overflow: hidden; padding: 0; }
.band img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.band::after { content: ""; position: absolute; inset: 0; background: rgba(7,20,48,.72); }
.band .wrap { position: relative; z-index: 2; text-align: center; }
.band h2 { color: #fff; }
.band p { color: rgba(255,255,255,.82); margin-left: auto; margin-right: auto; }

/* ---------- Service index ---------- */
.svc { border-top: 1px solid var(--line); }
.svc-item {
  display: grid; grid-template-columns: 60px 1fr 1.1fr; gap: 28px;
  padding: 32px 0; border-bottom: 1px solid var(--line); align-items: start;
}
.svc-num { font-weight: 700; color: var(--cyan-text); font-size: .92rem; padding-top: 5px; }
.svc-item h3 { margin: 0; }
.svc-item p { margin: 0; }

/* ---------- Gallery ---------- */
.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 4px; }
.gallery figure { margin: 0; overflow: hidden; background: var(--bg-alt); aspect-ratio: 1/1; cursor: pointer; }
/* Composed for exactly eight tiles. The old 7n+1 / 7n+4 rhythm was a repeating
   pattern for a 56-image wall; at eight it fires on tiles 1, 4 and 8 and leaves
   a hole in the last row. This is a fixed arrangement instead: one 2x2 opener,
   six squares, one wide closer. It packs with no gaps at 4, 3 and 2 columns,
   which are the only column counts minmax(290px) produces above the 900px
   breakpoint. If the gallery ever grows past eight again, this needs redoing,
   it is a composition, not a rule. */
.gallery figure:first-child { grid-column: span 2; grid-row: span 2; }
.gallery figure:last-child  { grid-column: span 2; aspect-ratio: 2/1; }
.gallery img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s cubic-bezier(.2,.7,.3,1); }
.gallery figure:hover img { transform: scale(1.05); }

.lightbox {
  position: fixed; inset: 0; background: rgba(6,16,38,.97);
  display: none; align-items: center; justify-content: center; z-index: 200; padding: 28px;
}
.lightbox.open { display: flex; }
.lightbox img { max-width: 94vw; max-height: 86vh; object-fit: contain; }
.lb-close, .lb-prev, .lb-next {
  position: absolute; background: none; color: #fff; border: 0;
  width: 56px; height: 56px; font-size: 1.7rem; cursor: pointer;
  display: flex; align-items: center; justify-content: center; opacity: .7;
}
.lb-close:hover, .lb-prev:hover, .lb-next:hover { opacity: 1; }
.lb-close { top: 18px; right: 18px; }
.lb-prev { left: 12px; top: 50%; transform: translateY(-50%); }
.lb-next { right: 12px; top: 50%; transform: translateY(-50%); }

/* ---------- Captioned photo strip (service pages) ---------- */
.photogrid {
  display: grid; gap: 20px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.photogrid figure { margin: 0; cursor: pointer; }
.photogrid img {
  width: 100%; aspect-ratio: 4/3; object-fit: cover;
  background: var(--bg-alt); display: block;
  transition: opacity .25s ease;
}
.photogrid figure:hover img { opacity: .88; }
.photogrid figcaption {
  margin-top: 10px; font-size: .88rem; line-height: 1.5; color: var(--body);
}
.photogrid figure:focus-visible { outline: 3px solid var(--cyan-text); outline-offset: 3px; }

/* ---------- Process ---------- */
.steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 44px; }
.step-n {
  font-size: .78rem; font-weight: 700; letter-spacing: .16em; color: var(--cyan-text);
  padding-bottom: 14px; margin-bottom: 18px; border-bottom: 2px solid var(--cyan-text);
  display: inline-block;
}
.step h3 { font-size: 1.15rem; }
.step p { margin: 0; font-size: .98rem; }

/* ---------- FAQ ---------- */
.faq-item { border-bottom: 1px solid var(--line); padding: 30px 0; }
.faq-item:first-child { border-top: 1px solid var(--line); }
.faq-item h2, .faq-item h3 { margin-bottom: .5em; font-size: 1.28rem; font-stretch: 108%; letter-spacing: -.015em; }
.faq-item p { margin: 0; }

/* ---------- Contact ---------- */
.contact { background: var(--navy-deep); color: rgba(255,255,255,.78); }
.contact h2, .contact h3 { color: #fff; }
.contact .eyebrow { color: var(--cyan); }
.contact-grid { display: grid; grid-template-columns: 1.1fr 1fr; gap: 76px; }
.field { margin-bottom: 20px; }
.field label {
  display: block; font-size: .76rem; letter-spacing: .13em; text-transform: uppercase;
  font-weight: 700; color: rgba(255,255,255,.55); margin-bottom: 9px;
}
.field input, .field select, .field textarea {
  width: 100%; padding: 15px 0; font: inherit; font-size: 1.02rem;
  background: transparent; color: #fff; border: 0;
  border-bottom: 1px solid rgba(255,255,255,.24);
}
.field select option { color: var(--navy-deep); }
.field input:focus, .field select:focus, .field textarea:focus { border-bottom-color: var(--cyan); }
.field textarea { min-height: 110px; resize: vertical; }
.hp { position: absolute; left: -9999px; }

.contact-line { display: flex; gap: 18px; padding: 22px 0; border-bottom: 1px solid rgba(255,255,255,.14); }
.contact-line:last-child { border-bottom: 0; }
.contact-line svg { width: 22px; height: 22px; color: var(--cyan); flex: 0 0 22px; margin-top: 5px; }
.contact-line small {
  display: block; font-size: .74rem; letter-spacing: .13em; text-transform: uppercase;
  color: rgba(255,255,255,.5); margin-bottom: 4px;
}
.contact-line a, .contact-line b {
  color: #fff; font-size: 1.32rem; font-weight: 700; text-decoration: none; letter-spacing: -.02em;
}
.contact-line a:hover { color: var(--cyan); }

.formmsg { padding: 15px 18px; margin-bottom: 18px; font-size: .96rem; }
.formmsg:empty { display: none; padding: 0; margin: 0; }
.formmsg.ok { background: rgba(120,180,120,.16); color: #A8E0A8; }
.formmsg.err { background: rgba(200,90,70,.18); color: #F0AE9A; }

/* ---------- Footer ---------- */
/* No background here: the .g-foot band paints it, and footer.site would win
   on specificity (0,1,1 vs 0,1,0) and reintroduce a hard edge. */
footer.site { color: rgba(255,255,255,.5); padding: 54px 0; font-size: .92rem; }
footer.site .wrap { display: flex; justify-content: space-between; gap: 20px; flex-wrap: wrap; align-items: center; }
footer.site a { color: rgba(255,255,255,.72); text-decoration: none; }
footer.site a:hover { color: var(--cyan); }


/* ---------- Form affordances ---------- */
.field label .opt { font-weight: 400; letter-spacing: .06em; opacity: .7; }
.formnote {
  margin: 14px 0 0; font-size: .88rem; line-height: 1.5;
  color: rgba(255,255,255,.55);
}

/* Contextual CTA between persuasion blocks. Sits on light surfaces, so the
   primary must be .btn-rust -- .btn-line is white-on-transparent and would be
   invisible here. */
.rowcta {
  display: flex; align-items: center; gap: 16px 18px; flex-wrap: wrap;
  margin: 0 0 108px; font-size: 1.1rem; color: var(--navy-2); font-weight: 600;
}
.rowcta .btn { padding: 14px 26px; font-size: .98rem; }
.rowcta span { font-weight: 400; }

/* ---------- Gallery filter ---------- */
.galfilter {
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
  margin: 0 0 22px; padding: 0 24px;
}
.galfilter button {
  font: inherit; font-size: .9rem; font-weight: 600; cursor: pointer;
  padding: 9px 16px; border: 1.5px solid var(--line); background: transparent;
  color: var(--body); transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.galfilter button:hover { border-color: var(--cyan-text); color: var(--cyan-text); }
.galfilter button[aria-pressed="true"] {
  background: var(--navy-deep); border-color: var(--navy-deep); color: #fff;
}


/* ---------- The descent ----------
   One continuous surface from daylight to deep water. Every band below ends on
   exactly the colour the next one begins with, so there is no seam anywhere on
   the page. This is the product's own argument -- one continuous layer, no
   joints -- made structural, and it is why the page must never go back to
   stacked blocks with hard background changes between them.

   The steep part of the descent happens across .dive, which is the gallery:
   photographs carry the mid-tones, so no body copy ever sits on a value where
   neither dark nor light text is safe. Keep it that way. */
.g-1    { background: linear-gradient(180deg, #FFFFFF 0%, #F4F8FC 100%); }
.g-2    { background: linear-gradient(180deg, #F4F8FC 0%, #EAF1F8 100%); }
.g-3    { background: linear-gradient(180deg, #EAF1F8 0%, #E3ECF5 100%); }
/* Pages stack several light bands. Repeating .g-3 would restart the ramp, so
   later light sections hold the colour the previous one ended on. */
.g-3 ~ .g-3 { background: #E3ECF5; }
/* For short pages that go hero -> one light section -> dive: spans the whole
   light range in one band so the dive still starts on #E3ECF5. */
.g-mid  { background: linear-gradient(180deg, #F4F8FC 0%, #E3ECF5 100%); }

/* The descent itself. A band with NO TEXT IN IT, on purpose: this is the only
   stretch of the page where the value passes through the mid-tones, and no
   colour of type is legible across that whole range. Everything above it reads
   dark-on-light; everything below reads light-on-dark. Do not put copy here. */
.dive { height: 150px; background: linear-gradient(180deg, #E3ECF5 0%, #A9BDD6 30%, #52709C 66%, #1E3763 100%); }

.g-4    { background: linear-gradient(180deg, #1E3763 0%, #17305A 100%); }
.g-5    { background: linear-gradient(180deg, #17305A 0%, #122A50 100%); }
.g-6    { background: linear-gradient(180deg, #122A50 0%, #0E2347 100%); }
.g-7    { background: linear-gradient(180deg, #0E2347 0%, #0A1D42 100%); }
.g-end  { background: linear-gradient(180deg, #17305A 0%, #0A1D42 100%); }
.g-deep { background: linear-gradient(180deg, #1E3763 0%, #0A1D42 100%); }
.g-foot { background: linear-gradient(180deg, #0A1D42 0%, #071630 100%); }

/* Which way the type reads on a given band. */
.on-light { color: var(--body); }
.on-light h1, .on-light h2, .on-light h3 { color: var(--navy); }
.on-light .eyebrow { color: var(--cyan-text); }

.on-dark  { color: rgba(255,255,255,.82); }
.on-dark h1, .on-dark h2, .on-dark h3, .on-dark strong, .on-dark b { color: #fff; }
.on-dark .eyebrow { color: var(--cyan-dk); }
/* Must not reach buttons: this is (0,1,1) and .btn-rust is (0,1,0), so without
   the :not() every gold CTA on a dark band renders white-on-gold at 2.16:1. */
.on-dark a:not(.btn) { color: #fff; }
.on-dark .lead { color: rgba(255,255,255,.88); }
.on-dark .failures li { border-bottom-color: rgba(255,255,255,.16); }
.on-dark .failures li::before { background: var(--cyan); }
.on-dark .faq-item { border-color: rgba(255,255,255,.16); }
.on-dark .svc-item { border-color: rgba(255,255,255,.16); }
.on-dark .svc-num { color: var(--cyan-dk); }
.on-dark .step-n { color: var(--cyan-dk); border-bottom-color: var(--cyan); }
.on-dark .photogrid figcaption { color: rgba(255,255,255,.72); }
.on-dark .rowcta { color: rgba(255,255,255,.86); }
.on-dark .rowcta span { color: rgba(255,255,255,.86); }
.on-dark .rowcta a:not(.btn) { color: var(--cyan-dk); }
.on-dark .rowlink a { color: var(--cyan-dk); }

/* ---------- Surface and substrate ----------
   The light bands carry large-format tile in running bond, which is what a
   shower is actually finished in; a square grid reads as graph paper. It fades
   page descends, and the deep bands pick up the mottled wash of a membrane.
   You begin standing on the finished floor and go down to what is underneath
   it, which is the thing this company actually sells. Both layers sit behind
   content at z-index 0 and neither carries meaning. */
.g-1, .g-2, .g-3, .g-mid { position: relative; }
.g-1::before, .g-2::before, .g-3::before, .g-mid::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22300%22%20height%3D%22300%22%20viewBox%3D%220%200%20300%20300%22%3E%3Cg%20fill%3D%22%2523143478%22%20fill-opacity%3D%220.075%22%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%22300%22%20height%3D%223%22%2F%3E%3Crect%20x%3D%220%22%20y%3D%22150%22%20width%3D%22300%22%20height%3D%223%22%2F%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%223%22%20height%3D%22150%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22150%22%20width%3D%223%22%20height%3D%22150%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
  background-repeat: repeat;
}
/* The fade is CHAINED across the bands, not restarted in each one. Each band
   picks up the opacity the previous band ended on, so the tile thins out once,
   continuously, over the whole light zone. Give a band its own 1.0-to-0 mask
   and the floor appears to start again halfway down the page. */
.g-1::before   { -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,1)   0%, rgba(0,0,0,.80) 100%);
                         mask-image: linear-gradient(to bottom, rgba(0,0,0,1)   0%, rgba(0,0,0,.80) 100%); }
.g-2::before   { -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,.80) 0%, rgba(0,0,0,.55) 100%);
                         mask-image: linear-gradient(to bottom, rgba(0,0,0,.80) 0%, rgba(0,0,0,.55) 100%); }
.g-3::before   { -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,.55) 0%, rgba(0,0,0,0)   100%);
                         mask-image: linear-gradient(to bottom, rgba(0,0,0,.55) 0%, rgba(0,0,0,0)   100%); }
.g-mid::before { -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,.80) 0%, rgba(0,0,0,0)   100%);
                         mask-image: linear-gradient(to bottom, rgba(0,0,0,.80) 0%, rgba(0,0,0,0)   100%); }
.g-4, .g-5, .g-6, .g-7, .g-end, .g-deep { position: relative; }
.g-4::before, .g-5::before, .g-6::before, .g-7::before,
.g-end::before, .g-deep::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    radial-gradient(ellipse 70% 40% at 18% 22%, rgba(255,255,255,.045), transparent 60%),
    radial-gradient(ellipse 55% 50% at 72% 58%, rgba(255,255,255,.038), transparent 62%),
    radial-gradient(ellipse 80% 35% at 45% 88%, rgba(255,255,255,.030), transparent 60%);
}
/* Content sits above both layers. */
.g-1 > *, .g-2 > *, .g-3 > *, .g-mid > *,
.g-4 > *, .g-5 > *, .g-6 > *, .g-7 > *, .g-end > *, .g-deep > * {
  position: relative; z-index: 1;
}

/* ---------- The up-turn ----------
   A membrane runs across the floor and turns up the wall to a level line. That
   corner is the most recognisable thing in the trade, so it marks the start of
   a section. Drawn from the eyebrow, which every section already begins with. */
.eyebrow { position: relative; padding-top: 36px; }
.eyebrow::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 24px;
  border-bottom: 2px solid currentColor;
  border-left: 2px solid currentColor;
  opacity: .42; pointer-events: none;
}
/* The opening of a page has no section above it to turn up from, whether that
   opening is the home hero or an interior masthead. */
.hero-copy .eyebrow, .masthead .eyebrow { padding-top: 0; }
.hero-copy .eyebrow::before, .masthead .eyebrow::before { display: none; }
/* On sub-pages the mark has to be one fixed width. Left to `right: 0` it takes
   the width of whatever contains it, which since the spine landed is 336px in a
   .sec-head and 780px in a heading block above a full-width row: the same glyph
   at two lengths on one page. Pinning it to the spine column makes it read as a
   mark rather than a rule that happens to measure the container. */
.masthead ~ section .eyebrow::before { right: auto; width: min(21rem, 100%); }

/* ---------- Depth gauge ----------
   A single unbroken rule down the left edge, ticked at each section and filled
   to your scroll position. It is the seamless argument made visible -- one
   continuous line, no joints -- and it reads as sounding depth, which is what
   the page is doing. Decorative: aria-hidden, and it never carries meaning that
   is not also in the content. */
.depth {
  position: fixed; left: 30px; top: 0; bottom: 0; width: 1px; z-index: 70;
  pointer-events: none; display: none;
}
.depth-rail { position: absolute; inset: 0; background: currentColor; opacity: .16; }
.depth-fill {
  position: absolute; left: 0; top: 0; width: 1px; height: 0;
  background: var(--cyan); opacity: .9;
}
.depth-tick {
  position: absolute; left: -5px; width: 11px; height: 1px;
  background: currentColor; opacity: .3;
}
.depth-tick.on { background: var(--cyan); opacity: 1; }
@media (min-width: 1180px) { .depth { display: block; } }
@media (prefers-reduced-motion: reduce) { .depth-fill { transition: none !important; } }

/* ---------- Atmosphere ---------- */

/* Woven roving — the mat under the resin is a grid of crossing bundles. Held
   at the threshold of visible on purpose; it should read as the tooth of a
   material, never as a pattern. */
.weave {
  background-image:
    repeating-linear-gradient(45deg,  rgba(20,52,120,.030) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(-45deg, rgba(20,52,120,.030) 0 1px, transparent 1px 7px);
}

/* Reveal on scroll. The .in class is added by js/effects.js, and is applied
   unconditionally when motion is reduced or IntersectionObserver is missing --
   content must never depend on this to be visible. */
.rise {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .62s cubic-bezier(.22,.68,.3,1), transform .62s cubic-bezier(.22,.68,.3,1);
  will-change: opacity, transform;
}
.rise.in { opacity: 1; transform: none; will-change: auto; }


/* A link that is the only thing in its paragraph is a control, not inline text,
   so it does not get WCAG 2.5.8's inline exception. Links inside a sentence are
   untouched by this selector, which is the distinction that matters. */
p > a:only-child { display: inline-block; padding: 4px 0; min-height: 24px; }
.rowlink a { padding: 5px 0; min-height: 24px; display: inline-block; }
/* Footer numbers. An office line and a cell are different things to someone
   deciding who to ring, so each is labelled and iconed. The SVGs carry no
   width/height attributes, so they MUST be sized here or they fall back to the
   default replaced-element size and grow with the viewport. */
.foot-tel { display: flex; align-items: center; gap: 8px 22px; flex-wrap: wrap; }
.foot-tel a { display: inline-flex; align-items: center; gap: 8px; padding: 5px 0; min-height: 24px; }
.foot-tel svg { width: 17px; height: 17px; flex: 0 0 17px; opacity: .75; }
.foot-tel em {
  font-style: normal; font-size: .72rem; font-weight: 700; font-stretch: 88%;
  letter-spacing: .12em; text-transform: uppercase; opacity: .6; margin-right: 5px;
}

/* ---------- Accessibility ---------- */
.skip {
  position: absolute; left: 8px; top: -70px; z-index: 300;
  background: var(--navy-deep); color: #fff; padding: 12px 18px;
  font-weight: 700; text-decoration: none; transition: top .15s ease;
}
.skip:focus { top: 8px; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Zero-specificity default so component rules can still opt out. */
:where(a, button, [role="button"], input, select, textarea):focus-visible {
  outline: 3px solid var(--cyan);
  outline-offset: 2px;
}
.gallery figure:focus-visible { outline: 3px solid var(--cyan); outline-offset: -3px; }
#loadmore[disabled] { opacity: .55; cursor: default; transform: none; }

/* ---------- Responsive ---------- */
@media (max-width: 900px) {
  section { padding: 68px 0; }
  .row { grid-template-columns: 1fr; gap: 32px; margin-bottom: 62px; }
  .rowcta { margin-bottom: 62px; font-size: 1rem; }
  .row.flip .row-media { order: 0; }
  .contact-grid { grid-template-columns: 1fr; gap: 48px; }
  .svc-item { grid-template-columns: 40px 1fr; gap: 16px; }
  .svc-item p { grid-column: 2; }
  .gallery { grid-template-columns: repeat(2, 1fr); }
  /* At two columns a 2x2 opener is a full-width square taller than the phone,
     so the opener drops to a plain wide tile and everything else is square. */
  .gallery figure:first-child { grid-column: span 2; grid-row: auto; aspect-ratio: 2/1; }
  .gallery figure:last-child  { grid-column: span 2; aspect-ratio: 2/1; }
  .stat { padding: 30px 20px 30px 0; }
  .hero { min-height: 88vh; }
  .hero .wrap { padding-bottom: 30px; padding-top: 44px; }
  .hero-sub .wrap { padding-top: 108px; padding-bottom: 44px; }
  /* Both hero CTAs must clear the fixed call bar on a 375x667 screen.
     Verified 2026-07: 130px clearance on the primary, 60px on the secondary.
     The hero lead is deliberately short -- it carried the years/pan-count/
     warranty proof once, which duplicated the stats strip below and cost
     four lines of height, pushing both buttons under the bar. Re-measure
     at exactly 375x667 if you lengthen it. */
  .hero-cta { margin-top: 22px; }
  .hero-cta .btn { padding: 14px 22px; }
  nav.main {
    display: none; position: absolute; top: 100%; left: 0; right: 0;
    background: var(--navy-deep); flex-direction: column; align-items: stretch; gap: 0; padding: 8px 24px 22px;
  }
  nav.main.open { display: flex; }
  nav.main a { padding: 15px 0; border-bottom: 1px solid rgba(255,255,255,.12); }
  nav.main .btn { margin-top: 14px; justify-content: center; }
  .navtoggle { display: block; }
  .callbar { display: block; }
  .faq-item a[href^="tel:"], footer.site a[href^="tel:"] { display: inline-block; padding: 10px 2px; }
  footer.site .wrap > span { display: block; }
  .contact-line a { display: inline-block; padding: 6px 0; }
  .navtoggle { padding: 9px; }
  body { padding-bottom: var(--callbar-h); }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
  .rise { opacity: 1 !important; transform: none !important; }
  .gallery figure:hover img { transform: none !important; }
}
