/* ============================================================================
   DEXPERTS — mydexperts.com
   Palette and type are governed by the DEXPERTS brand guidelines:
   two colours (#000000, #7C390B) and one typeface (Poppins). Everything else
   below is an explicit screen derivation, not new brand colour.
   ========================================================================== */

:root{
  /* Brand — from the guidelines, do not alter */
  --ink:#000000;
  --wood:#7C390B;

  /* Derived neutrals */
  --char:#141210;      /* warm near-black, for large dark panels */
  --bone:#F7F5F2;      /* warm off-white, alternating sections */
  --grey:#5E5E5E;      /* body text on light */
  --line:#E3DED8;      /* hairlines and card borders */
  --paper:#FFFFFF;

  --display:'Poppins',system-ui,-apple-system,'Segoe UI',sans-serif;
  --wrap:1200px;
  --head-h:78px;

  /* The logo's X arms run at this angle; reused for every striped surface. */
  --board-angle:40deg;
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;
  font-family:var(--display);
  font-weight:400;
  font-size:17px;
  line-height:1.7;
  color:var(--grey);
  background:var(--paper);
  overflow-x:hidden;
}
@media(min-width:900px){body{font-size:18px}}

img{max-width:100%;height:auto;display:block}
a{color:var(--wood);text-decoration:none}
a:hover{text-decoration:underline}

h1,h2,h3,h4{
  font-family:var(--display);
  color:var(--ink);
  margin:0;
  line-height:1.08;
}
h1{font-weight:800;font-size:clamp(38px,5.5vw,68px);letter-spacing:-.03em;line-height:1.04}
h2{font-weight:700;font-size:clamp(28px,3.6vw,44px);letter-spacing:-.02em;line-height:1.12}
h3{font-weight:600;font-size:clamp(20px,2vw,30px);letter-spacing:-.01em;line-height:1.2}
p{margin:0 0 18px}
p:last-child{margin-bottom:0}

.wrap{width:100%;max-width:var(--wrap);margin:0 auto;padding:0 28px}
@media(max-width:620px){.wrap{padding:0 20px}}

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

/* --- the tracked uppercase micro-label: the connective tissue of the design */
.label{
  display:block;
  font-size:13px;
  font-weight:600;
  text-transform:uppercase;
  letter-spacing:.14em;
  color:var(--wood);
  margin:0 0 14px;
}
.label--light{color:rgba(255,255,255,.72)}

:focus-visible{outline:3px solid var(--wood);outline-offset:3px;border-radius:2px}
.on-dark :focus-visible{outline-color:#fff}

/* ============================ buttons ==================================== */
.btn{
  display:inline-flex;align-items:center;gap:10px;
  font-family:var(--display);font-size:16px;font-weight:600;
  padding:15px 30px;border-radius:999px;   /* full pill */
  border:1.5px solid transparent;
  cursor:pointer;text-decoration:none;
  transition:transform .18s ease,background .18s ease,color .18s ease,border-color .18s ease;
}
.btn:hover{text-decoration:none;transform:translateY(-2px)}
.btn:active{transform:translateY(0)}
.btn svg{flex:none}

.btn-primary{background:var(--wood);color:#fff}
.btn-primary:hover{background:#672F09;color:#fff}

.btn-line{background:transparent;color:var(--ink);border-color:var(--ink)}
.btn-line:hover{background:var(--ink);color:#fff}

.on-dark .btn-line{color:#fff;border-color:rgba(255,255,255,.55)}
.on-dark .btn-line:hover{background:#fff;color:var(--ink);border-color:#fff}

/* The call button is the site's one deliberate motion moment. */
.btn-call{position:relative;animation:callGlow 3s ease-in-out infinite}
@keyframes callGlow{
  0%,100%{box-shadow:0 0 0 0 rgba(124,57,11,.42)}
  50%{box-shadow:0 0 0 12px rgba(124,57,11,0)}
}
.btn-call:hover svg{animation:callWiggle .5s ease}
@keyframes callWiggle{
  0%,100%{transform:rotate(0)}25%{transform:rotate(-14deg)}75%{transform:rotate(14deg)}
}

/* --- shimmer button ------------------------------------------------------
   Vanilla adaptation of the shadcn/Magic-UI <ShimmerButton>. The original masks
   a spinning conic gradient down to a ring using an opaque inset backdrop -- a
   technique that needs a filled button, because the backdrop IS the fill. This
   pill has no fill, so the backdrop is gone and the shimmer became the thing it
   can still be on clear glass: a soft band of light sweeping across the face,
   clipped to the pill, over a static hairline border. Same --sb-speed cadence
   as the original.
   (The obvious alternative -- keeping the ring via a content-box mask-composite
   -- does not survive a 999px radius: the inner mask box has square corners, so
   the ring vanishes across both end caps.) */
.btn-shimmer{
  --sb-shimmer:rgba(255,255,255,.55);
  --sb-radius:999px;
  --sb-speed:3s;
  position:relative;z-index:0;overflow:hidden;
  /* Inherits .btn's 1.5px transparent border and 15px 30px padding, so it still
     measures exactly the same as the call button beside it. */
  border-color:rgba(255,255,255,.55);   /* matches .on-dark .btn-line */
  background:transparent;color:#fff;
}
.btn-shimmer:hover,.btn-shimmer:focus-visible{
  background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.82);color:#fff;
}
/* The sweep. Negative z-index inside the button's own stacking context, so it
   paints over the (transparent) background but under the label -- the text never
   dims as the band passes beneath it. */
.btn-shimmer .sb-spark{
  position:absolute;inset:0;z-index:-1;
  border-radius:var(--sb-radius);overflow:hidden;pointer-events:none;
}
.btn-shimmer .sb-spark i{
  position:absolute;top:-50%;bottom:-50%;left:0;width:52%;
  transform:skewX(-18deg);
  background:linear-gradient(90deg,transparent,var(--sb-shimmer),transparent);
  filter:blur(6px);
  animation:sbSweep var(--sb-speed) ease-in-out infinite;
}
/* Holding the end position from 60% on leaves a beat of stillness between
   passes, rather than a band shuttling back and forth without pause. */
@keyframes sbSweep{
  0%{left:-60%}
  60%,100%{left:104%}
}

/* Trailing arrow. Nudges on hover, and stands still for reduced motion. */
.btn-arrow{transition:transform .22s ease}
.btn:hover .btn-arrow{transform:translateX(4px)}

/* ============================ header ===================================== */
.site-head{
  position:fixed;inset:0 0 auto 0;z-index:80;height:var(--head-h);
  display:flex;align-items:center;
  background:rgba(255,255,255,.92);
  backdrop-filter:saturate(180%) blur(14px);
  border-bottom:1px solid var(--line);
}
.site-head .wrap{display:flex;align-items:center;justify-content:space-between;gap:24px}
.brand{display:flex;align-items:center;flex:none}
.brand img{height:32px;width:auto}
@media(max-width:620px){.brand img{height:26px}}

.nav-links{display:flex;align-items:center;gap:30px;list-style:none;margin:0;padding:0}
.nav-links a{
  font-size:15px;font-weight:600;color:var(--ink);text-decoration:none;
  letter-spacing:.01em;position:relative;padding:6px 0;
}
.nav-links a::after{
  content:"";position:absolute;left:0;right:100%;bottom:0;height:2px;
  background:var(--wood);transition:right .25s ease;
}
.nav-links a:hover::after,.nav-links a[aria-current="page"]::after{right:0}
.nav-links a[aria-current="page"]{color:var(--wood)}

.nav-cta{
  display:inline-flex;align-items:center;gap:9px;flex:none;
  background:var(--wood);color:#fff;font-size:15px;font-weight:600;
  padding:11px 22px;border-radius:999px;text-decoration:none;
  /* Set in caps here rather than in the markup, so the label is still announced
     as a normally-cased phrase. Caps need the tracking opened a little. */
  text-transform:uppercase;letter-spacing:.06em;
  transition:background .18s ease,transform .18s ease;
}
.nav-cta:hover{background:#672F09;color:#fff;text-decoration:none;transform:translateY(-1px)}

.head-right{display:flex;align-items:center;gap:12px;flex:none}

.nav-toggle{
  display:none;width:44px;height:44px;border:1px solid var(--line);background:#fff;
  border-radius:10px;cursor:pointer;padding:0;
}
.nav-toggle span{display:block;width:20px;height:2px;background:var(--ink);margin:4px auto;transition:.25s}
.nav-toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(6px) rotate(45deg)}
.nav-toggle[aria-expanded="true"] span:nth-child(2){opacity:0}
.nav-toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-6px) rotate(-45deg)}

/* This breakpoint used to hide the label outright, because the label was a
   13-character phone number. "CALL US" is short enough to keep, and a labelled
   button converts better than a bare glyph -- but it is still ~75px wider than
   the icon-only pill it replaces, and brand + button + menu toggle share one
   row. So the width comes back out of everything that ISN'T the label: the icon
   goes (the pill is a tel: link and reads as a call button without it), the type
   tightens, the logo steps down, and the row gap closes. The label is what the
   client asked for, so it is the last thing that would ever be cut. */
@media(max-width:480px){
  .nav-cta{padding:11px 14px;font-size:13px;gap:0}
  .nav-cta svg{display:none}
  .site-head .wrap{gap:14px}
  .brand img{height:23px}
}
/* Small phones (320-360). Same idea, one notch further. */
@media(max-width:360px){
  .site-head .wrap{gap:10px}
  .nav-cta{padding:10px 12px}
  .brand img{height:20px}
}

@media(max-width:940px){
  .nav-toggle{display:block}
  .nav-wrap{
    position:absolute;top:var(--head-h);left:0;right:0;
    background:#fff;border-bottom:1px solid var(--line);
    max-height:0;overflow:hidden;transition:max-height .3s ease;
  }
  .nav-wrap.open{max-height:520px}
  .nav-links{flex-direction:column;align-items:flex-start;gap:0;padding:10px 28px 22px}
  .nav-links li{width:100%;border-bottom:1px solid var(--line)}
  .nav-links li:last-child{border-bottom:0}
  .nav-links a{display:block;padding:15px 0;font-size:17px;width:100%}
  .nav-links a::after{display:none}
}

/* ============================ hero ======================================= */
.hero{
  position:relative;min-height:100svh;display:flex;align-items:center;
  padding:calc(var(--head-h) + 40px) 0 70px;
  background:var(--char);overflow:hidden;
}
.hero-slides{position:absolute;inset:0;z-index:0}
.hero-slide{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity 1.8s ease;
}
.hero-slide.active{opacity:1}
.hero::before{
  content:"";position:absolute;inset:0;z-index:1;
  background:
    linear-gradient(180deg,rgba(20,18,16,.72) 0%,rgba(20,18,16,.42) 45%,rgba(20,18,16,.86) 100%);
}
.hero .wrap{position:relative;z-index:2}
.hero-inner{display:flex;flex-direction:column;align-items:center;text-align:center}
.hero h1{margin:0}
.hero-logo{width:min(92vw,880px);height:auto;filter:drop-shadow(0 4px 22px rgba(0,0,0,.6))}
@media(max-width:620px){.hero-logo{width:min(88vw,420px)}}

.hero .lede{
  margin:26px 0 0;max-width:44ch;
  color:rgba(255,255,255,.92);font-size:clamp(17px,2vw,21px);font-weight:400;
  text-shadow:0 2px 14px rgba(0,0,0,.5);
  /* Caps, set here so screen readers still get a normally-cased sentence. The
     tracking has to be opened or Poppins jams at this size.
     No indent compensation: the trailing letter-space does not land inside the
     measured glyph box here, so the line already centres exactly -- adding one
     was measured pushing it 1.05px right. */
  text-transform:uppercase;letter-spacing:.1em;
}
.hero-cta{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;margin-top:clamp(34px,5vw,54px)}
.hero-meta{
  margin-top:clamp(34px,5vw,54px);
  display:flex;flex-wrap:wrap;gap:10px 26px;justify-content:center;
  font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  color:rgba(255,255,255,.7);
}
.hero-meta span{display:inline-flex;align-items:center;gap:10px}
.hero-meta span::before{content:"";width:6px;height:6px;background:var(--wood);flex:none}

/* ============================ page banner ================================ */
.page-banner{
  position:relative;display:flex;align-items:center;
  /* Equidistant title: the visible gap above the h1 matches the gap below the
     content. padding-top carries the fixed 78px header on top of that shared
     58px, hence the asymmetric raw values. align-items:center (not flex-end)
     is what keeps the two gaps equal -- with flex-end, any slack from
     min-height lands entirely above the title. Keep the floor below the
     natural height so it adds no slack at normal widths. */
  min-height:220px;
  padding:calc(var(--head-h) + 58px) 0 58px;
  overflow:hidden;
  /* The deck-board striping lives on the element itself so the X can sit in a
     pseudo-element ABOVE it and still fall UNDER the scrim -- stripes, then X,
     then scrim, then text. */
  background:
    repeating-linear-gradient(var(--board-angle),#1D1916 0 13px,var(--char) 13px 28px),
    var(--char);
}
/* The ground used to be a 2000x1000 raster (images/banner-*.jpg, five identical
   copies) drawn with object-fit:cover. Baked into it was a CROP OF THE WORDMARK,
   not the X mark -- 340x261 of artwork against the real mark's 361x308, which is
   how it kept the E and P stems while clipping the X's own points. The striping
   moved to .page-banner above, rebuilt from that raster's measured values so the
   texture is unchanged. The jpgs stay on disk; each page still cites one as its
   og:image. */

/* The X, centred on the TITLE rather than on the section box. Insetting the top
   edge by --head-h is what does it: the box carries padding-top:calc(78px + 58px)
   to clear the fixed header against only 58px below, so its geometric centre sits
   39px above the title -- which is why the old raster read high. Dropping this
   layer by the header height puts its centre at 136px + content/2, exactly the
   title's centre, and holds there at every width. */
.page-banner::after{
  content:"";position:absolute;inset:var(--head-h) 0 0;z-index:0;
  background:url(images/x-mark-light.png?v=1) no-repeat center;
  /* Sized against THIS LAYER, not the viewport. A vw-based clamp is what let it
     reach 217px inside a 187px layer on desktop and get sliced top and bottom --
     the exact chopped look this change existed to remove. A percentage of the
     layer's own height cannot overflow it by construction, and 82% keeps a
     visible margin above and below the arms at every width. */
  background-size:auto 82%;
  /* .32 over the stripes, measured to land near the RGB(95,95,90) the strokes
     rendered at in the old raster. It reads brighter than that if it paints over
     the scrim, which is why z-index puts it under. */
  opacity:.32;pointer-events:none;
}
/* Scrim last, over both the stripes and the X -- the raster it replaced sat
   under this gradient too. */
.page-banner::before{
  content:"";position:absolute;inset:0;z-index:1;
  background:linear-gradient(180deg,rgba(20,18,16,.62) 0%,rgba(20,18,16,.5) 40%,rgba(20,18,16,.9) 100%);
}
.page-banner .wrap{position:relative;z-index:2}
/* Caps are set here rather than typed into the markup, so screen readers still
   receive a normally-cased heading. The base h1 tracking is -.03em, cut for
   lowercase at 68px, and caps need it opened back out. */
.page-banner h1{
  color:#fff;text-shadow:0 3px 18px rgba(0,0,0,.45);
  text-transform:uppercase;
  /* Was the base h1's 800. Poppins ExtraBold in caps at 68px is heavy and round
     enough to read as bubbly, which the client rejected; 600 is the weight
     .cta-band h2 already uses, so the two caps treatments on a page now agree.
     Lighter strokes leave the letters looking tighter, so the tracking opens
     further than the .01em that 800 wanted. */
  font-weight:600;letter-spacing:.04em;
}
.page-banner p{margin:16px 0 0;max-width:58ch;color:rgba(255,255,255,.88)}
.page-banner--center .wrap{text-align:center}
.page-banner--center p{margin-left:auto;margin-right:auto}

/* ============================ sections =================================== */
section.block{padding:clamp(64px,8vw,110px) 0}
section.block--tight{padding-top:clamp(40px,5vw,68px)}
.block--bone{background:var(--bone)}
.block--char{background:var(--char);color:rgba(255,255,255,.78)}
.block--char h2,.block--char h3{color:#fff}

.sec-head{max-width:760px;margin:0 0 clamp(34px,4vw,54px)}
.sec-head--center{margin-left:auto;margin-right:auto;text-align:center}
.sec-head p{margin-top:16px;font-size:clamp(17px,1.6vw,19px)}
/* A sec-head stripped back to just its supporting line should not inherit the
   gap that sat under the heading it no longer has. */
.sec-head > p:first-child{margin-top:0}
/* A sec-head's bottom margin exists to clear whatever it introduces. Standing
   alone in a wrap -- as on About's "Where we work" -- it would leave ~51px of
   dead space and sit the section visibly high in its own padding. */
.wrap > .sec-head:last-child{margin-bottom:0}
.block--char .sec-head p{color:rgba(255,255,255,.76)}

.story{max-width:760px}
.story--center{margin-left:auto;margin-right:auto;text-align:center}
/* Justified column, per the About page reference. text-wrap:pretty keeps the
   last line of each paragraph from stranding a single word. */
.story--justify{margin-left:auto;margin-right:auto;text-align:justify;text-wrap:pretty}
@media(max-width:560px){.story--justify{text-align:left}}
.story p{font-size:clamp(17px,1.7vw,19px);margin-bottom:20px}
.story h3{margin:38px 0 14px}

/* The X motif as a rule — the site's signature divider. */
.x-rule{display:flex;align-items:center;gap:18px;margin:0 0 30px}
.x-rule::before,.x-rule::after{content:"";height:1px;background:var(--line);flex:1}
.x-rule img{height:26px;width:auto;flex:none}
.block--char .x-rule::before,.block--char .x-rule::after{background:rgba(255,255,255,.18)}

/* Deck-board striping, at the same angle as the logo's X arms. */
.boarded{
  background-image:repeating-linear-gradient(
    var(--board-angle),
    rgba(124,57,11,.14) 0 10px,
    transparent 10px 26px);
}

/* ============================ cards ====================================== */
.grid{display:grid;gap:22px}
.grid-2{grid-template-columns:repeat(2,1fr)}
.grid-3{grid-template-columns:repeat(3,1fr)}
.grid-4{grid-template-columns:repeat(4,1fr)}
@media(max-width:980px){.grid-3,.grid-4{grid-template-columns:repeat(2,1fr)}}
@media(max-width:640px){.grid-2,.grid-3,.grid-4{grid-template-columns:1fr}}

.card{
  background:var(--paper);border:1px solid var(--line);border-radius:16px;
  padding:32px 28px;transition:border-color .2s ease,transform .2s ease;
}
.card:hover{border-color:var(--wood);transform:translateY(-3px)}
.card h3{margin-bottom:12px}
.card p{font-size:16px;margin:0}
/* Cards carried a single paragraph until the services copy grew; without this
   the stacked paragraphs run together, since .card p zeroes all margins. */
.card p + p{margin-top:14px}
.card-num{
  display:block;font-size:13px;font-weight:700;letter-spacing:.14em;
  color:var(--wood);margin-bottom:16px;
}

/* ============================ steps ====================================== */
.steps{counter-reset:step;display:grid;gap:26px;grid-template-columns:repeat(3,1fr)}
@media(max-width:820px){.steps{grid-template-columns:1fr}}
.step{padding-top:26px;border-top:2px solid var(--wood)}
.step-n{
  display:flex;align-items:center;gap:12px;
  font-size:13px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--wood);margin-bottom:14px;
}
.step-n img{height:16px;width:auto}
.step h3{margin-bottom:10px}
.step p{font-size:16px;margin:0}
.block--char .step{border-top-color:var(--wood)}

/* ============================ materials ================================== */
.tiers{display:grid;gap:24px;grid-template-columns:repeat(4,1fr)}
@media(max-width:1180px){.tiers{grid-template-columns:repeat(2,1fr)}}
@media(max-width:640px){.tiers{grid-template-columns:1fr}}
.tier{
  background:var(--paper);border:1px solid var(--line);border-radius:18px;
  overflow:hidden;display:flex;flex-direction:column;
  transition:border-color .2s ease,transform .2s ease;
}
.tier:hover{border-color:var(--wood);transform:translateY(-3px)}
.tier--best{border-color:var(--wood);border-width:2px}
.tier-swatch{
  height:132px;background:var(--char);position:relative;
  background-image:repeating-linear-gradient(
    var(--board-angle),
    rgba(124,57,11,.85) 0 16px,
    rgba(20,18,16,.9) 16px 34px);
}
.tier--better .tier-swatch{
  background-image:repeating-linear-gradient(
    var(--board-angle),
    rgba(124,57,11,.5) 0 16px,
    rgba(20,18,16,.92) 16px 34px);
}
.tier--best .tier-swatch{
  background-image:repeating-linear-gradient(
    var(--board-angle),
    rgba(255,255,255,.16) 0 16px,
    rgba(20,18,16,.95) 16px 34px);
}
.tier--wood .tier-swatch{
  background-image:repeating-linear-gradient(
    var(--board-angle),
    rgba(124,57,11,.72) 0 11px,
    rgba(58,26,8,.94) 11px 24px);
}

/* Railing swatches. Same card and swatch geometry as the decking tiers above, but
   the stripes run vertical instead of at --board-angle, so a railing row reads as
   balusters at a glance rather than boards. Separate class names on purpose --
   reusing .tier--good and friends would drag the diagonal board gradients along,
   and .tier--best would drag its highlight border too. */
.rail--wood .tier-swatch{
  background-image:repeating-linear-gradient(
    90deg,
    rgba(124,57,11,.85) 0 14px,
    rgba(20,18,16,.92) 14px 34px);
}
.rail--alu .tier-swatch{
  background-image:repeating-linear-gradient(
    90deg,
    rgba(255,255,255,.34) 0 7px,
    rgba(20,18,16,.94) 7px 28px);
}
.rail--syn .tier-swatch{
  background-image:repeating-linear-gradient(
    90deg,
    rgba(124,57,11,.42) 0 18px,
    rgba(20,18,16,.92) 18px 40px);
}
/* Cable reads horizontally -- thin bright lines under tension, not pickets. */
.rail--view .tier-swatch{
  background-image:repeating-linear-gradient(
    180deg,
    rgba(255,255,255,.42) 0 2px,
    rgba(20,18,16,.96) 2px 17px);
}
.tier-rank{
  position:absolute;top:16px;left:18px;
  background:var(--wood);color:#fff;
  font-size:12px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  padding:6px 14px;border-radius:999px;
}
.tier-body{padding:28px;display:flex;flex-direction:column;flex:1}
@media(min-width:1181px){.tier-body{padding:26px 20px}}
.tier-body h3{margin-bottom:10px}
/* Second line inside a tier title, for the specific materials behind a general
   name. Set in the same tracked uppercase micro-type as .label so it reads as a
   caption rather than as part of the heading -- appended inline instead, the
   title ran to three lines against one or two on the neighbouring cards. */
.tier-sub{
  display:block;margin-top:8px;
  font-size:13px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;
  color:var(--wood);line-height:1.3;
}
.tier-body>p{font-size:16px}
.tier-facts{list-style:none;margin:20px 0 0;padding:18px 0 0;border-top:1px solid var(--line)}
.tier-facts li{
  display:flex;justify-content:space-between;gap:16px;
  font-size:15px;padding:7px 0;
}
.tier-facts b{color:var(--ink);font-weight:600;text-align:right}
.budget{letter-spacing:.16em;color:var(--wood);font-weight:700}
.budget .off{color:var(--line)}

.cmp-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}
table.cmp{width:100%;min-width:780px;border-collapse:collapse;background:var(--paper)}
table.cmp th,table.cmp td{
  text-align:left;padding:16px 18px;border-bottom:1px solid var(--line);font-size:16px;
}
table.cmp thead th{
  font-size:13px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink);border-bottom:2px solid var(--ink);
}
table.cmp tbody th{font-weight:600;color:var(--ink);white-space:nowrap}
table.cmp tbody tr:last-child th,table.cmp tbody tr:last-child td{border-bottom:0}

/* ============================ portfolio ================================== */
/* Tight photo mosaic, per the Lakeshore projects grid: 6px gutters, square
   corners, uniform 4:3 tiles. No labels or overlays -- the photos carry it.
   The grid lives OUTSIDE .wrap so it runs the full width of the viewport;
   .pf-block strips the section padding so it butts against the banner. */
section.pf-block{padding:6px 0}   /* 6px = the tile gutter, so the banner divider matches the grid */
.pf-grid{display:grid;gap:6px;grid-template-columns:repeat(3,1fr)}
@media(max-width:1024px){.pf-grid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:640px){.pf-grid{grid-template-columns:1fr}}
.pf-item{position:relative;margin:0;aspect-ratio:4/3;overflow:hidden;background:var(--char)}
.pf-bg{display:block;width:100%;height:100%;object-fit:cover;object-position:center}

/* ============================ FAQ ======================================== */
.faq{max-width:840px}
.faq--center{margin:0 auto}
.faq details{border-bottom:1px solid var(--line)}
.faq summary{
  list-style:none;cursor:pointer;padding:22px 44px 22px 0;position:relative;
  font-size:clamp(17px,1.8vw,20px);font-weight:600;color:var(--ink);
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
  content:"";position:absolute;right:8px;top:50%;width:12px;height:12px;
  border-right:2px solid var(--wood);border-bottom:2px solid var(--wood);
  transform:translateY(-70%) rotate(45deg);transition:transform .22s ease;
}
.faq details[open] summary::after{transform:translateY(-30%) rotate(-135deg)}
.faq details p{padding:0 44px 24px 0;margin:0;font-size:16px}

/* ============================ CTA band =================================== */
.cta-band{position:relative;background:var(--char);color:#fff;overflow:hidden}
.cta-band::before{
  content:"";position:absolute;inset:0;
  background-image:repeating-linear-gradient(
    var(--board-angle),
    rgba(124,57,11,.22) 0 14px,
    transparent 14px 40px);
}
.cta-band .wrap{position:relative;z-index:1;text-align:center;padding-top:clamp(64px,8vw,104px);padding-bottom:clamp(64px,8vw,104px)}
/* Set in caps here rather than in the markup, so screen readers still get a
   normally-cased sentence. The base h2 tracking is negative for lowercase;
   caps need it opened back up or the words jam together. */
.cta-band h2{
  color:#fff;margin:0 auto;
  font-weight:600;      /* Poppins SemiBold -- was the base h2 700 */
  font-size:32px;       /* 24pt. Flat, not fluid: a pt spec is a fixed size. */
  text-transform:uppercase;letter-spacing:.015em;
  /* Was max-width:20ch, which is what forced the two-line stack. Removed so
     the line runs flat; balance only kicks in on a phone, where 30 caps
     characters cannot fit on one line at any weight. */
  max-width:none;text-wrap:balance;
}
.cta-band p{margin:18px auto 0;max-width:52ch;color:rgba(255,255,255,.8)}
.cta-actions{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;margin-top:34px}
/* The supporting paragraph that used to sit between the heading and the buttons
   is gone. Holding its exact 113px open left a hole, so the gap is now scaled
   off the band padding instead, with matching air under the buttons. */
.cta-band .cta-actions{margin-top:clamp(30px,3.6vw,42px)}
/* Scoped to .cta-band so it outranks the generic .cta-band p margin above. */
.cta-band .recept{
  margin-top:clamp(26px,3vw,32px);font-size:14px;letter-spacing:.06em;color:rgba(255,255,255,.62);
}

/* ============================ contact / form ============================= */
.contact-grid{display:grid;gap:44px;grid-template-columns:1fr 1.15fr}
@media(max-width:940px){.contact-grid{grid-template-columns:1fr}}
.cline{display:flex;gap:16px;padding:22px 0;border-bottom:1px solid var(--line)}
.cline:first-child{border-top:1px solid var(--line)}
/* The card is now the wrap's first child, so :first-child no longer lands on
   a .cline and the top hairline would go missing. */
.callout-cta + .cline{border-top:1px solid var(--line)}

.cline svg{flex:none;margin-top:3px;color:var(--wood)}
.cline h3{font-size:15px;text-transform:uppercase;letter-spacing:.12em;font-weight:600;margin-bottom:6px}
.cline a{font-size:19px;font-weight:600;color:var(--ink)}
/* An address has no space or hyphen to break at, so a long one can be wider
   than the contact column on a 375px phone and push the page sideways.
   jasko@mydexperts.com is short enough to fit; this stays as a guard for
   whatever address replaces it. */
a[href^="mailto:"]{overflow-wrap:anywhere}
@media(max-width:480px){.cline a[href^="mailto:"]{font-size:17px}}
.cline a:hover{color:var(--wood)}
.cline .subnote{display:block;font-size:14px;color:var(--grey);margin-top:4px}

/* --- call card -----------------------------------------------------------
   Lakeshore's contact callout rebuilt in this palette: --char panel rather
   than navy, wood pill rather than blue. No overflow:hidden -- .btn-call's
   glow pulses 12px past its edge and clipping it would kill the animation. */
.callout-cta{
  background:var(--char);color:rgba(255,255,255,.78);
  border-radius:18px;padding:32px 34px;margin-bottom:30px;
}
/* An h2 so the page keeps h1 -> h2 -> h3 order; sized to the card, not to the
   clamp(28px,3.6vw,44px) a section heading would take. */
.callout-cta h2{font-size:26px;color:#fff;letter-spacing:-.01em}
.callout-cta > p{color:rgba(255,255,255,.8);font-size:15px;margin:10px 0 22px}
.callout-cta .btn{white-space:nowrap}
.callout-alt{margin:18px 0 0;font-size:14px;color:rgba(255,255,255,.7)}
.callout-alt a{color:#fff;font-weight:600;text-decoration:underline}
.callout-alt a:hover{color:#fff}
@media(max-width:480px){.callout-cta{padding:26px 22px}.callout-cta h2{font-size:23px}}


form.quote{background:var(--paper);border:1px solid var(--line);border-radius:18px;padding:clamp(24px,3vw,38px)}
.field{margin-bottom:18px}
.field label{display:block;font-size:14px;font-weight:600;color:var(--ink);margin-bottom:7px}
.field .req{color:var(--wood)}
.field input,.field select,.field textarea{
  width:100%;font-family:var(--display);font-size:16px;color:var(--ink);
  padding:13px 15px;border:1px solid var(--line);border-radius:10px;background:#fff;
  transition:border-color .18s ease;
}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--wood)}
.field textarea{min-height:132px;resize:vertical}
.field-row{display:grid;gap:18px;grid-template-columns:1fr 1fr}
@media(max-width:620px){.field-row{grid-template-columns:1fr}}
.form-note{font-size:14px;margin:16px 0 0}
.form-ok{
  display:none;background:var(--bone);border:1px solid var(--wood);
  border-radius:14px;padding:26px;text-align:center;
}
.form-ok.show{display:block}
.form-ok h3{margin-bottom:8px}

/* Honeypot wrapper -- see the note on the field in contact.html. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
/* Shown only when a send actually failed. Wood-adjacent red so it reads as a
   problem without importing a colour from outside the palette. */
.form-err{
  display:none;margin:16px 0 0;font-size:14px;
  color:#8A2B0F;background:#FBEDE7;border:1px solid #E7C3B4;
  border-radius:10px;padding:12px 14px;
}
.form-err.show{display:block}
.form-err a{color:#8A2B0F;font-weight:600;text-decoration:underline}
.btn:disabled{opacity:.62;cursor:default;transform:none;animation:none}


/* ============================ map ======================================== */
/* Full-bleed strip sitting flush between the last section and the footer.
   Two side-by-side maps split by a standing X rule. The middle grid column
   is the divider itself, so the channel it occupies IS the breathing room
   that tells you these are two separate places, not one wide map. */
.map-block{background:var(--bone);padding-top:clamp(46px,5.5vw,68px)}
.map-split{display:grid;grid-template-columns:1fr auto 1fr;gap:0}
.map-wrap{position:relative;height:440px;background:var(--bone)}
.map-wrap iframe{width:100%;height:100%;border:0;display:block;filter:grayscale(.2) contrast(1.02)}

/* The X rule on its side. Same hairlines and same 26px mark -- only the axis
   changes, so the divider is recognisably the one used everywhere else. */
.x-rule--v{flex-direction:column;gap:18px;margin:0;width:clamp(46px,5vw,64px)}
.x-rule--v::before,.x-rule--v::after{width:1px;height:auto}

/* Stacked once half the screen is too narrow to read a map in -- and with the
   maps above and below each other, the rule lies back down between them. */
@media(max-width:860px){
  .map-split{grid-template-columns:1fr}
  .map-wrap{height:340px}
  .x-rule--v{flex-direction:row;width:auto;padding:30px 20px}
  .x-rule--v::before,.x-rule--v::after{width:auto;height:1px}
}
@media(max-width:620px){.map-wrap{height:300px}}

/* ============================ footer ===================================== */
.site-foot{background:var(--char);color:rgba(255,255,255,.62);padding:64px 0 30px;font-size:15px}
.foot-top{display:grid;gap:38px;grid-template-columns:1.4fr 1fr 1fr;padding-bottom:42px;border-bottom:1px solid rgba(255,255,255,.14)}
@media(max-width:860px){.foot-top{grid-template-columns:1fr 1fr}}
@media(max-width:560px){.foot-top{grid-template-columns:1fr}}
.foot-logo{height:34px;width:auto;margin-bottom:18px}
.site-foot h4{
  color:#fff;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  margin:0 0 16px;
}
.site-foot ul{list-style:none;margin:0;padding:0}
.site-foot li{margin-bottom:10px}
.site-foot a{color:rgba(255,255,255,.72)}
.site-foot a:hover{color:#fff}
.foot-base{
  display:flex;flex-wrap:wrap;gap:12px;justify-content:space-between;
  padding-top:24px;font-size:14px;color:rgba(255,255,255,.45);
}

/* ============================ motion ===================================== */
/* The transition is declared on the base state, not on .in, so it is never
   introduced in the same frame as the value change it is meant to animate. */
html.js .reveal{opacity:0;transform:translateY(14px);transition:opacity .6s ease,transform .6s ease}
html.js .reveal.in{opacity:1;transform:none}
.d1{transition-delay:.08s}.d2{transition-delay:.16s}.d3{transition-delay:.24s}

@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
  html.js .reveal{opacity:1;transform:none}
  .btn-call{animation:none}
  .btn-shimmer .sb-spark{display:none}
}
