/* Nashville's Best Pros — clean marketplace.
 *
 * Listings vary enormously in quality: some businesses will upload eight good
 * photographs, some will upload none. So the chrome stays quiet and the card
 * degrades well — a listing with no image gets a lettered tile rather than a
 * broken box, and nothing in the layout assumes photography exists.
 *
 * Montserrat + Roboto, matching the rest of the estate.
 */

@font-face{font-family:'Montserrat';src:url(/assets/fonts/Montserrat-ExtraBold.woff2) format('woff2');font-weight:700 900;font-style:normal;font-display:swap}
@font-face{font-family:'Roboto';src:url(/assets/fonts/Roboto-Regular.woff2) format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Roboto';src:url(/assets/fonts/Roboto-Bold.woff2) format('woff2');font-weight:600 800;font-style:normal;font-display:swap}

:root{
  --paper:#FFFFFF;
  --sunk:#F6F7F9;
  --ink:#14171C;
  --ink-2:#41474F;
  --muted:#697079;
  --rule:#E4E7EB;
  --rule-2:#CBD1D8;
  --brand:#1B6EF3;
  --brand-ink:#1355C4;
  --brand-tint:#EEF4FE;
  --ok:#15803D;
  --err:#B91C1C;
  --r:10px;
  --tap:44px;
  --pad:clamp(1.25rem,5vw,2rem);
  --wide:74rem;
  --display:'Montserrat';
  --font:'Roboto';
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--paper, #ffffff);color:var(--ink, #12161c);
  font-family:var(--font),system-ui,-apple-system,'Segoe UI',sans-serif;
  font-size:1rem;line-height:1.6;
}
img{max-width:100%;height:auto;display:block}
a{color:var(--brand-ink, #0b4ea2)}
h1,h2,h3,h4{font-family:var(--display),var(--font),sans-serif;font-weight:800;line-height:1.15;letter-spacing:-.02em;margin:0 0 .5rem}
h1{font-size:clamp(1.9rem,5vw,3rem)}
h2{font-size:clamp(1.4rem,3.2vw,2rem)}
h3{font-size:1.1rem}
p{margin:0 0 1rem;text-wrap:pretty}
.lede{font-size:clamp(1.05rem,2.2vw,1.2rem);color:var(--ink-2, #39424e);line-height:1.55;max-width:62ch}
.muted{color:var(--muted, #6b7684)}
.wrap{width:100%;max-width:var(--wide);margin-inline:auto;padding-inline:var(--pad)}
.narrow{max-width:44rem}
section{padding-block:clamp(2.25rem,6vw,3.75rem)}
.sec-sunk{background:var(--sunk, #f5f7fa);border-block:1px solid var(--rule, #e3e8ef)}
.sec-head{max-width:52rem;margin-bottom:1.5rem}
.mt-md{margin-top:1.5rem}
.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}

/* ---- Buttons -------------------------------------------------------------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.4rem;
  min-height:var(--tap);padding:.65rem 1.2rem;border-radius:var(--r);
  font-weight:700;font-size:.95rem;text-decoration:none;cursor:pointer;
  border:1.5px solid transparent;
}
.btn-primary{background:var(--brand, #1a73e8);border-color:var(--brand, #1a73e8);color:#fff}
.btn-primary:hover{background:var(--brand-ink, #0b4ea2);border-color:var(--brand-ink, #0b4ea2)}
.btn-ghost{background:transparent;border-color:var(--rule-2, #cbd3df);color:var(--ink, #12161c)}
.btn-ghost:hover{border-color:var(--ink, #12161c);background:var(--sunk, #f5f7fa)}
.btn-link{background:none;border:0;padding:0;color:var(--brand-ink, #0b4ea2);font:inherit;cursor:pointer;text-decoration:underline}

/* ---- Header --------------------------------------------------------------- */
.hdr{position:sticky;top:0;z-index:60;background:rgba(255,255,255,.94);backdrop-filter:saturate(1.4) blur(8px);border-bottom:1px solid var(--rule, #e3e8ef)}
.hdr-in{display:flex;align-items:center;gap:1rem;min-height:64px}
.brand{display:flex;align-items:center;flex:none;text-decoration:none;padding-block:.5rem}
/* 160/136, the same as every other site in the estate. Fixed-ratio image, so
   width alone sizes it and the height follows. */
.brand img{width:160px;height:auto;display:block}
@media (max-width:26rem){.brand img{width:136px}}
.nav{display:none;margin-left:auto;align-items:center;gap:.35rem}
.nav a:not(.btn){text-decoration:none;color:var(--ink-2, #39424e);font-weight:600;font-size:.95rem;padding:.6rem .8rem;border-radius:var(--r);min-height:var(--tap);display:inline-flex;align-items:center}
.nav a:not(.btn):hover{color:var(--ink, #12161c);background:var(--sunk, #f5f7fa)}
.nav a:not(.btn)[aria-current=page]{color:var(--ink, #12161c);box-shadow:inset 0 -2px 0 var(--brand, #1a73e8)}
.nav .btn{margin-left:.5rem;padding-inline:1.1rem}
.burger{margin-left:auto;width:var(--tap);height:var(--tap);display:grid;place-content:center;gap:5px;background:none;border:1px solid var(--rule-2, #cbd3df);border-radius:var(--r);cursor:pointer}
.burger span{display:block;width:19px;height:2px;background:var(--ink, #12161c);transition:transform .2s,opacity .2s}
.burger[aria-expanded=true] span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.burger[aria-expanded=true] span:nth-child(2){opacity:0}
.burger[aria-expanded=true] span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
.drawer{display:none;border-top:1px solid var(--rule, #e3e8ef);background:var(--paper, #ffffff);padding:.75rem var(--pad) 1.5rem}
.drawer.open{display:block}
.drawer a:not(.btn){display:flex;align-items:center;min-height:50px;text-decoration:none;color:var(--ink, #12161c);font-weight:600;border-bottom:1px solid var(--rule, #e3e8ef)}
.drawer .btn{width:100%;margin-top:.9rem}
@media (min-width:62rem){
  .burger,.drawer{display:none!important}
  .nav{display:flex}
}

/* ---- Hero + finder -------------------------------------------------------- */
.hero{background:linear-gradient(170deg,var(--brand-tint, #e8f0fe) 0%,var(--paper, #ffffff) 70%);border-bottom:1px solid var(--rule, #e3e8ef)}
.hero h1{max-width:18ch}
.hero-note{margin-top:1rem;font-size:.9rem;color:var(--muted, #6b7684)}
.finder{display:grid;gap:.75rem;margin-top:1.75rem;padding:1rem;background:var(--paper, #ffffff);border:1px solid var(--rule, #e3e8ef);border-radius:calc(var(--r) + 4px);box-shadow:0 10px 30px rgb(20 23 28 / .06)}
.finder-field{display:grid;gap:.3rem;min-width:0}
.finder-field label{font-size:.82rem;font-weight:700;color:var(--ink-2, #39424e)}
.finder select{width:100%;min-height:var(--tap);padding:.6rem .75rem;border:1.5px solid var(--rule-2, #cbd3df);border-radius:var(--r);background:var(--paper, #ffffff);color:var(--ink, #12161c);font:inherit;font-size:.95rem}
.finder .btn{min-height:var(--tap)}
@media (min-width:48rem){
  .finder{grid-template-columns:1fr 1fr auto;align-items:end}
}

.page-head{background:var(--sunk, #f5f7fa);border-bottom:1px solid var(--rule, #e3e8ef);padding-block:clamp(1.75rem,4vw,2.75rem)}
.crumbs{font-size:.8rem;color:var(--muted, #6b7684);margin-bottom:.6rem}
.crumbs a{color:var(--muted, #6b7684);text-decoration:none}
.crumbs a:hover{color:var(--ink, #12161c)}

/* ---- Cards ---------------------------------------------------------------- */
.pro-grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fill,minmax(16rem,1fr))}
.pro-card{
  display:flex;flex-direction:column;text-decoration:none;color:inherit;
  border:1px solid var(--rule, #e3e8ef);border-radius:calc(var(--r) + 2px);overflow:hidden;
  background:var(--paper, #ffffff);transition:border-color .15s,transform .15s,box-shadow .15s;
}
.pro-card:hover{border-color:var(--rule-2, #cbd3df);transform:translateY(-2px);box-shadow:0 10px 26px rgb(20 23 28 / .08)}
.pro-shot{display:block;aspect-ratio:3/2;background:var(--sunk, #f5f7fa);position:relative;overflow:hidden}
.pro-shot img{width:100%;height:100%;object-fit:cover;object-position:50% 40%}
/* No photograph is a normal state, not a broken one. */
.card-fallback{
  position:absolute;inset:0;display:grid;place-content:center;
  font-family:var(--display);font-weight:800;font-size:2rem;color:var(--brand, #1a73e8);
  background:var(--brand-tint, #e8f0fe);letter-spacing:.02em;
}
.pro-body{display:flex;flex-direction:column;gap:.25rem;padding:.9rem 1rem 1.1rem}
.pro-name{font-family:var(--display);font-weight:800;font-size:1.05rem;line-height:1.2}
.pro-meta{font-size:.82rem;color:var(--muted, #6b7684)}
.pro-tag{font-size:.9rem;color:var(--ink-2, #39424e);line-height:1.45;margin-top:.15rem}
.pro-badges{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:.5rem}
.badge{font-size:.7rem;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--brand-ink, #0b4ea2);background:var(--brand-tint, #e8f0fe);border-radius:999px;padding:.2rem .55rem}
.result-count{font-size:.9rem;color:var(--muted, #6b7684);margin-bottom:1rem}

.cat-grid{display:grid;gap:.6rem;grid-template-columns:repeat(auto-fill,minmax(11rem,1fr))}
.cat-chip{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.7rem .9rem;border:1px solid var(--rule, #e3e8ef);border-radius:var(--r);text-decoration:none;color:var(--ink, #12161c);font-weight:600;font-size:.92rem}
.cat-chip:hover{border-color:var(--brand, #1a73e8);background:var(--brand-tint, #e8f0fe)}
.cat-chip small{color:var(--muted, #6b7684);font-weight:400;font-size:.78rem}
.city-grid{display:grid;gap:.6rem;grid-template-columns:repeat(auto-fill,minmax(10rem,1fr))}
.city-chip{display:flex;flex-direction:column;padding:.7rem .9rem;border:1px solid var(--rule, #e3e8ef);border-radius:var(--r);text-decoration:none;color:var(--ink, #12161c);font-weight:600}
.city-chip:hover{border-color:var(--brand, #1a73e8);background:var(--brand-tint, #e8f0fe)}
.city-chip small{color:var(--muted, #6b7684);font-weight:400;font-size:.78rem}
.cat-list{display:grid;gap:1px;background:var(--rule, #e3e8ef);border:1px solid var(--rule, #e3e8ef);border-radius:var(--r);overflow:hidden}
.cat-row{display:grid;gap:.2rem;padding:.9rem 1.1rem;background:var(--paper, #ffffff);text-decoration:none;color:inherit;grid-template-columns:1fr auto}
.cat-row:hover{background:var(--sunk, #f5f7fa)}
.cat-row-name{font-weight:700}
.cat-row-blurb{grid-column:1;font-size:.88rem;color:var(--muted, #6b7684);line-height:1.45}
.cat-row-count{grid-row:1;grid-column:2;font-size:.85rem;color:var(--muted, #6b7684)}

.empty{text-align:center;padding:3rem 1rem;border:1px dashed var(--rule-2, #cbd3df);border-radius:calc(var(--r) + 4px);background:var(--sunk, #f5f7fa)}
.empty h2{font-size:1.2rem}

/* ---- Profile -------------------------------------------------------------- */
.profile-head.has-hero{padding-top:0}
.profile-hero{width:100%;height:clamp(160px,26vw,300px);object-fit:cover;object-position:50% 40%}
.profile-id{display:flex;gap:1rem;align-items:center;margin-top:1rem}
.profile-logo{width:72px;height:72px;flex:none;border-radius:var(--r);object-fit:cover;background:var(--paper, #ffffff);border:1px solid var(--rule, #e3e8ef)}
.profile-logo-fallback{display:grid;place-content:center;font-family:var(--display);font-weight:800;font-size:1.5rem;color:var(--brand, #1a73e8);background:var(--brand-tint, #e8f0fe);border-color:transparent}
.profile-sub{margin:.15rem 0 .4rem;color:var(--muted, #6b7684);font-size:.92rem}
.profile-badges{margin:0;display:flex;flex-wrap:wrap;gap:.35rem}
.profile-cols{display:grid;gap:2rem;padding-block:clamp(1.75rem,4vw,2.75rem)}
.profile-main section{padding-block:0;margin-bottom:2rem}
.profile-main h2{font-size:1.25rem;margin-bottom:.6rem}
.tag-list{list-style:none;padding:0;margin:0;display:flex;flex-wrap:wrap;gap:.4rem}
.tag-list li{background:var(--sunk, #f5f7fa);border:1px solid var(--rule, #e3e8ef);border-radius:999px;padding:.3rem .7rem;font-size:.85rem}
.tag-list li a{text-decoration:none;color:var(--ink, #12161c)}
.gallery{display:grid;gap:.6rem;grid-template-columns:repeat(auto-fill,minmax(12rem,1fr))}
.gallery figure{margin:0}
.gallery img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--r);border:1px solid var(--rule, #e3e8ef)}
.gallery figcaption{font-size:.8rem;color:var(--muted, #6b7684);margin-top:.3rem}
.contact-card{border:1px solid var(--rule, #e3e8ef);border-radius:calc(var(--r) + 4px);padding:1.25rem;background:var(--paper, #ffffff);box-shadow:0 10px 30px rgb(20 23 28 / .05)}
.contact-card h2{font-size:1.1rem}
.contact-line{margin:0 0 .4rem;font-weight:700}
@media (min-width:62rem){
  .profile-cols{grid-template-columns:minmax(0,1.7fr) minmax(19rem,1fr)}
  .profile-aside{position:sticky;top:88px;align-self:start}
}

/* ---- Forms ---------------------------------------------------------------- */
.form{display:grid;gap:.9rem}
.form-stack{max-width:26rem}
.field{display:grid;gap:.3rem;min-width:0}
.field label{font-weight:700;font-size:.88rem}
.field .opt{font-weight:400;color:var(--muted, #6b7684)}
.field input,.field select,.field textarea{
  width:100%;min-height:var(--tap);padding:.65rem .8rem;
  background:var(--paper, #ffffff);color:var(--ink, #12161c);border:1.5px solid var(--rule-2, #cbd3df);
  border-radius:var(--r);font:inherit;font-size:.95rem;
}
.field textarea{min-height:7rem;resize:vertical}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--brand, #1a73e8);outline:2px solid var(--brand-tint, #e8f0fe);outline-offset:1px}
.field-hint{font-size:.84rem;color:var(--muted, #6b7684);line-height:1.5;margin:.2rem 0 0}
.form-note{font-size:.84rem;color:var(--muted, #6b7684);margin:.2rem 0 0}
.form-status{display:none;padding:.7rem .9rem;border-radius:var(--r);font-weight:600;font-size:.92rem;margin:0}
.form-status.ok{display:block;background:#ECFDF3;color:var(--ok);border:1px solid #A7F3D0}
.form-status.err{display:block;background:#FEF2F2;color:var(--err);border:1px solid #FCA5A5}
.notice{padding:.75rem 1rem;border-radius:var(--r);font-weight:600;margin-bottom:1rem}
.notice-ok{background:#ECFDF3;color:var(--ok);border:1px solid #A7F3D0}
.notice-err{background:#FEF2F2;color:var(--err);border:1px solid #FCA5A5}

.dash-form fieldset{border:1px solid var(--rule, #e3e8ef);border-radius:calc(var(--r) + 2px);padding:1.25rem;margin:0 0 1.25rem;display:grid;gap:.9rem}
.dash-form legend{font-family:var(--display);font-weight:800;font-size:1rem;padding-inline:.4rem}
.check-row{display:flex;flex-wrap:wrap;gap:1rem}
.check-grid{display:grid;gap:.5rem;grid-template-columns:repeat(auto-fill,minmax(11rem,1fr))}
.check{display:flex;align-items:center;gap:.5rem;font-size:.92rem;min-height:var(--tap)}
.check input{width:18px;height:18px;flex:none;accent-color:var(--brand, #1a73e8)}
.dash-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:.5rem}
.dash-photos{padding-block:2rem;border-top:1px solid var(--rule, #e3e8ef);margin-top:2rem}
.photo-strip{display:grid;gap:.75rem;grid-template-columns:repeat(auto-fill,minmax(10rem,1fr));margin-bottom:1.5rem}
.photo-item{margin:0;display:grid;gap:.35rem}
.photo-item img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--r);border:1px solid var(--rule, #e3e8ef)}
.form-inline{max-width:32rem}
@media (min-width:48rem){
  .dash-form fieldset{grid-template-columns:1fr 1fr}
  .dash-form legend{grid-column:1/-1}
  .dash-form .field:not(.half){grid-column:1/-1}
  .dash-form .check-grid,.dash-form .check-row,.dash-form .field-hint{grid-column:1/-1}
}

/* ---- Footer --------------------------------------------------------------- */
.ftr{background:var(--ink, #12161c);color:#C3CAD3;padding-block:2.5rem 1.5rem;margin-top:0}
.ftr a{color:#C3CAD3;text-decoration:none}
.ftr a:hover{color:#fff}
.ftr-top{display:grid;gap:2rem}
.ftr-logo{width:190px;height:auto;display:block;margin-bottom:.9rem}
.ftr-blurb{font-size:.9rem;line-height:1.55;max-width:36ch;margin:0}
.ftr-h{font-size:.75rem;letter-spacing:.12em;text-transform:uppercase;color:#8B94A0;margin:0 0 .7rem}
.ftr ul{list-style:none;padding:0;margin:0;display:grid;gap:.4rem;font-size:.92rem}
.ftr-btm{display:flex;flex-wrap:wrap;gap:.6rem 1.5rem;justify-content:space-between;border-top:1px solid #2A2F37;margin-top:2rem;padding-top:1.25rem;font-size:.84rem;color:#8B94A0}
@media (min-width:52rem){.ftr-top{grid-template-columns:1.6fr 1fr 1fr}}

.prose h2{margin-top:1.75rem}
.prose ol,.prose ul{padding-left:1.25rem}
.prose li{margin-bottom:.5rem}

/* ---- FAQ (AEO) ------------------------------------------------------------
   Rendered as <details> so the answers are in the DOM for a crawler and a
   screen reader whether or not they are expanded, rather than injected on
   click. Mobile-first: the summary is a full-width tap target. */
.faq{display:grid;gap:1px;background:var(--rule, #e3e8ef);border:1px solid var(--rule, #e3e8ef);border-radius:var(--r);overflow:hidden}
.faq details{background:var(--paper, #ffffff)}
.faq summary{
  display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:1rem 1.1rem;min-height:var(--tap);cursor:pointer;
  font-weight:700;font-size:.98rem;line-height:1.4;list-style:none;
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
  content:'';flex:none;width:10px;height:10px;
  border-right:2px solid var(--muted, #6b7684);border-bottom:2px solid var(--muted, #6b7684);
  transform:rotate(45deg);transition:transform .18s ease;margin-top:-4px;
}
.faq details[open] summary::after{transform:rotate(-135deg);margin-top:2px}
.faq summary:hover{background:var(--sunk, #f5f7fa)}
.faq-a{padding:0 1.1rem 1.1rem}
.faq-a p{margin:0;color:var(--ink-2, #39424e);line-height:1.6}

/* ---- Mobile-first corrections --------------------------------------------
   Checked at 360px, which is narrower than the iPhone the design was drawn
   on and where the real breakages show up. */
@media (max-width:26rem){
  .finder{padding:.85rem}
  .pro-grid{grid-template-columns:1fr}
  .cat-grid,.city-grid{grid-template-columns:1fr 1fr}
  .cat-chip,.city-chip{font-size:.88rem;padding:.6rem .7rem}
  .cat-row{grid-template-columns:1fr auto}
  /* Long trade names — "Crawl Space Repair" — must wrap rather than force
     the grid wider than the screen. */
  .cat-chip span,.city-chip span{min-width:0;overflow-wrap:anywhere}
  .profile-id{flex-direction:column;align-items:flex-start;gap:.75rem}
  .gallery{grid-template-columns:1fr 1fr}
  .tag-list li{font-size:.8rem}
  .faq summary{font-size:.92rem;padding:.85rem .9rem}
  .faq-a{padding:0 .9rem .9rem}
}
/* A directory is browsed on a phone far more than on a desktop, so the
   listing grid gets a real two-up as soon as there is room for it. */
@media (min-width:30rem) and (max-width:47.99rem){
  .pro-grid{grid-template-columns:1fr 1fr}
}

/* ---- Tap targets ----------------------------------------------------------
   Measured at 360px: breadcrumb links came out 15px tall and footer links
   17px. WCAG 2.5.8 exempts links inline in a sentence, but neither of these
   is that — both are navigation someone taps with a thumb, and on a directory
   the footer is a primary way around the site. Padded to the 44px target
   without changing the visual rhythm, by growing the hit area rather than the
   type. */
.crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:.15rem .35rem}
.crumbs a,.crumbs span[aria-current]{
  display:inline-flex;align-items:center;min-height:var(--tap);padding-block:.25rem;
}
.ftr ul{gap:0}
.ftr ul li a{
  display:inline-flex;align-items:center;min-height:var(--tap);
}
.ftr-btm a{display:inline-flex;align-items:center;min-height:var(--tap)}

/* ---- Two-sided split ------------------------------------------------------
 *
 * The directory is two products: a search tool for homeowners and a profile
 * tool for the businesses. The homepage says so in one row rather than making
 * a contractor read homeowner copy to find out the listing is free.
 * Mobile-first — the lanes stack, and the homeowner lane stays first. */
.split{background:var(--sunk, #f5f7fa);border-block:1px solid var(--rule, #e3e8ef)}
.two-up{display:grid;gap:1.25rem;grid-template-columns:1fr}
.lane{
  background:var(--paper, #ffffff);border:1px solid var(--rule, #e3e8ef);border-radius:calc(var(--r) + 4px);
  padding:clamp(1.25rem,4vw,1.75rem);
}
.lane h2{margin:.15rem 0 .6rem;font-size:clamp(1.25rem,4vw,1.5rem)}
.lane p{color:var(--ink-2, #39424e);margin:0 0 1rem}
.lane .btn{width:100%}
.eyebrow{
  margin:0;font-family:var(--display),system-ui,sans-serif;font-weight:700;
  font-size:.72rem;letter-spacing:.09em;text-transform:uppercase;color:var(--brand-ink, #0b4ea2);
}
@media (min-width:48rem){
  .two-up{grid-template-columns:1fr 1fr;gap:1.5rem}
  .lane .btn{width:auto}
}

/* ---- Feature grid and steps (pro side) ----------------------------------- */
.feature-grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fill,minmax(16rem,1fr))}
.feature{border:1px solid var(--rule, #e3e8ef);border-radius:calc(var(--r) + 2px);padding:1.1rem;background:var(--paper, #ffffff)}
.feature h3{margin:0 0 .4rem;font-size:1.02rem}
.feature p{margin:0;color:var(--ink-2, #39424e);font-size:.94rem}
.steps{margin:0;padding-left:1.15rem;display:grid;gap:.7rem;max-width:52rem;color:var(--ink-2, #39424e)}
.steps li{padding-left:.2rem}
.steps strong{color:var(--ink, #12161c)}

/* ---- Popular searches -----------------------------------------------------
 *
 * Real query phrasing pointing at the same category pages the trade grid
 * points at. The duplication is the point: two sets of anchor text describing
 * one page is worth more than one, and this is the set people type. */
.term-list{list-style:none;padding:0;margin:0;display:flex;flex-wrap:wrap;gap:.45rem}
.term-list li{margin:0}
.term-list a{
  display:inline-block;min-height:var(--tap);line-height:calc(var(--tap) - 1.4rem);
  padding:.7rem .9rem;border:1px solid var(--rule, #e3e8ef);border-radius:999px;
  text-decoration:none;color:var(--ink-2, #39424e);font-size:.88rem;background:var(--paper, #ffffff);
}
.term-list a:hover{border-color:var(--brand, #1a73e8);background:var(--brand-tint, #e8f0fe);color:var(--brand-ink, #0b4ea2)}

/* The pro-side link sits apart from the homeowner links, the way Houzz and
   Angi both separate them — same nav, visibly a different audience. */
.nav-sep{position:relative;padding-left:1rem;margin-left:.35rem}
.nav-sep::before{content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);width:1px;height:1.1rem;background:var(--rule-2, #cbd3df)}

/* ---- Honeypot -------------------------------------------------------------
 *
 * Must be a class, never an inline style: the CSP is style-src 'self' with no
 * unsafe-inline, so a style attribute is discarded by the browser and the trap
 * renders as a visible box asking a real person to "leave empty". Which is
 * exactly what it did.
 *
 * Off-canvas rather than display:none — a bot that skips hidden fields is not
 * caught by one, and this stays in the layout as far as a script can tell.
 * The field name is meaningless on purpose: a semantic name like "company"
 * gets filled by the browser's own autofill and turns the trap on real users. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* An account-level notice: something the owner has to act on, not an error. */
.notice{
  border:1px solid var(--rule, #e3e8ef);border-left:3px solid var(--brand, #1a73e8);
  border-radius:var(--r);padding:1rem 1.1rem;background:var(--sunk, #f5f7fa);margin:.75rem 0;
}
.notice p{margin:0 0 .6rem;color:var(--ink-2, #39424e);font-size:.94rem}
.notice p:last-of-type{margin-bottom:.85rem}
.notice strong{color:var(--ink, #12161c)}

/* ---- Traffic dashboard ----------------------------------------------------
 *
 * Bar and sparkline sizes are classes rather than inline styles: the CSP is
 * style-src 'self' with no unsafe-inline, so a style attribute never reaches
 * the layout. Twenty-one width classes and twenty-one height classes in 5%
 * steps cover every bar this page can draw. */
.range{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center;margin-top:1rem}
.range a{padding:.45rem .8rem;border:1px solid var(--rule, #e3e8ef);border-radius:999px;text-decoration:none;color:var(--ink-2, #39424e);font-size:.86rem}
.range a:hover{border-color:var(--brand, #1a73e8);color:var(--brand-ink, #0b4ea2)}
.range a.is-on{background:var(--ink, #12161c);border-color:var(--ink, #12161c);color:#fff}
.range form{margin-left:auto}

.stat-grid{display:grid;gap:.75rem;grid-template-columns:repeat(auto-fit,minmax(9rem,1fr))}
.stat{border:1px solid var(--rule, #e3e8ef);border-radius:var(--r);padding:1rem;background:var(--paper, #ffffff)}
.stat-n{display:block;font-family:var(--display),system-ui,sans-serif;font-weight:800;font-size:1.6rem;line-height:1.1}
.stat-l{display:block;color:var(--muted, #6b7684);font-size:.82rem;margin-top:.2rem}

.grid-2up{display:grid;gap:2rem;grid-template-columns:1fr}
@media (min-width:52rem){.grid-2up{grid-template-columns:1fr 1fr;gap:2.5rem}}

.bars{list-style:none;margin:0;padding:0;display:grid;gap:.45rem;counter-reset:none}
.bars li{display:grid;grid-template-columns:minmax(0,11rem) 1fr auto;gap:.6rem;align-items:center;font-size:.86rem}
.bar-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink-2, #39424e)}
.bar-track{background:var(--sunk, #f5f7fa);border:1px solid var(--rule, #e3e8ef);border-radius:999px;height:.7rem;overflow:hidden}
.bar-fill{display:block;height:100%;background:var(--brand, #1a73e8);border-radius:999px}
.bar-n{font-variant-numeric:tabular-nums;color:var(--muted, #6b7684);font-size:.8rem}

.spark{list-style:none;margin:0;padding:0;display:flex;gap:2px;align-items:flex-end;overflow-x:auto}
.spark li{display:flex;flex-direction:column;align-items:center;gap:.25rem;min-width:1.1rem;flex:1 0 auto}
.spark-bar{display:block;width:100%;background:var(--brand, #1a73e8);border-radius:2px 2px 0 0;min-height:2px}
.spark li{height:9rem;justify-content:flex-end}
.spark-d{font-size:.6rem;color:var(--muted, #6b7684);white-space:nowrap}
.muted{color:var(--muted, #6b7684);font-size:.88rem}
.flash-err{border:1px solid var(--err);color:var(--err);border-radius:var(--r);padding:.7rem .9rem;margin:.75rem 0}

.w0{width:0%}
.w5{width:5%}
.w10{width:10%}
.w15{width:15%}
.w20{width:20%}
.w25{width:25%}
.w30{width:30%}
.w35{width:35%}
.w40{width:40%}
.w45{width:45%}
.w50{width:50%}
.w55{width:55%}
.w60{width:60%}
.w65{width:65%}
.w70{width:70%}
.w75{width:75%}
.w80{width:80%}
.w85{width:85%}
.w90{width:90%}
.w95{width:95%}
.w100{width:100%}

.spark-bar.h0{height:2%}
.spark-bar.h5{height:5%}
.spark-bar.h10{height:10%}
.spark-bar.h15{height:15%}
.spark-bar.h20{height:20%}
.spark-bar.h25{height:25%}
.spark-bar.h30{height:30%}
.spark-bar.h35{height:35%}
.spark-bar.h40{height:40%}
.spark-bar.h45{height:45%}
.spark-bar.h50{height:50%}
.spark-bar.h55{height:55%}
.spark-bar.h60{height:60%}
.spark-bar.h65{height:65%}
.spark-bar.h70{height:70%}
.spark-bar.h75{height:75%}
.spark-bar.h80{height:80%}
.spark-bar.h85{height:85%}
.spark-bar.h90{height:90%}
.spark-bar.h95{height:95%}
.spark-bar.h100{height:100%}

/* An alarm, not a stat: something is broken and the page should say so before
   it shows anything else. */
.alarm{border:1px solid var(--err);border-left:4px solid var(--err);border-radius:var(--r);padding:1.1rem 1.25rem;background:#FEF2F2}
.alarm h2{margin:0 0 .4rem;font-size:1.1rem;color:var(--err)}
.alarm p{margin:0 0 .9rem;color:var(--ink-2, #39424e);font-size:.92rem}
.alarm .bars li{grid-template-columns:minmax(0,12rem) minmax(0,1fr) auto}
.flash-ok{border:1px solid var(--ok);color:var(--ok);border-radius:var(--r);padding:.7rem .9rem;margin:0 0 .9rem}
.alarm form{margin:0 0 .9rem}

/* ---- Website score panel --------------------------------------------------
 * Shown to the business about its own site. Color carries the band, but the
 * number and the wording carry it too — color alone fails anyone who cannot
 * distinguish the hues, and this is the panel most likely to be acted on. */
.score-panel{border:1px solid var(--rule, #e3e8ef);border-left:4px solid var(--rule-2, #cbd3df);border-radius:var(--r);padding:1.1rem 1.25rem;margin:.75rem 0;background:var(--paper, #ffffff)}
.score-good{border-left-color:var(--ok)}
.score-fair{border-left-color:#B45309}
.score-poor{border-left-color:var(--err)}
.score-head{display:flex;gap:1rem;align-items:center}
.score-n{font-family:var(--display),system-ui,sans-serif;font-weight:800;font-size:2.4rem;line-height:1}
.score-good .score-n{color:var(--ok)}
.score-fair .score-n{color:#B45309}
.score-poor .score-n{color:var(--err)}
.score-head h3{margin:0 0 .2rem;font-size:1.02rem}
.score-head p{margin:0;color:var(--ink-2, #39424e);font-size:.92rem}
.score-sum{margin:.9rem 0 .4rem;font-size:.88rem;color:var(--ink-2, #39424e);font-weight:600}
.score-note{margin:.9rem 0 0;font-size:.82rem;color:var(--muted, #6b7684)}

/* Prospect list: score, business, site, failures. Four columns on desktop,
   stacked on a phone — this is a list read at a desk, but it must not break
   if it is opened on the way to a meeting. */
.prospects{list-style:none;margin:0;padding:0;display:grid;gap:.5rem}
.prospect{display:grid;gap:.15rem .9rem;grid-template-columns:auto 1fr;align-items:center;
  border:1px solid var(--rule, #e3e8ef);border-left:3px solid var(--rule-2, #cbd3df);border-radius:var(--r);padding:.7rem .9rem}
.prospect-good{border-left-color:var(--ok)}
.prospect-fair{border-left-color:#B45309}
.prospect-poor{border-left-color:var(--err)}
.prospect-n{font-family:var(--display),system-ui,sans-serif;font-weight:800;font-size:1.3rem;min-width:2.2rem}
.prospect-b{display:flex;flex-direction:column}
.prospect-b small{color:var(--muted, #6b7684);font-size:.78rem}
.prospect-w{grid-column:2;font-size:.82rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.prospect-w a{color:var(--brand-ink, #0b4ea2)}
@media (min-width:46rem){
  .prospect{grid-template-columns:auto minmax(0,1fr) minmax(0,1fr) auto}
  .prospect-w{grid-column:auto}
}

/* ---- Editorial layer ------------------------------------------------------
 *
 * Photography carries the page. Everything here exists to get out of its way:
 * type gets larger and airier, cards lose their borders in favor of the image
 * edge, and the grid loosens so each photograph has room to be looked at.
 *
 * The images are the same ones the trade sites use. A homeowner who sees a
 * plumbing photograph here and again on nashvillesplumbingrepair.com should be
 * looking at one operation rather than two that happen to share a market. */

/* ---- Photographic hero ---- */
.hero-photo{
  position:relative;min-height:min(72vh,560px);display:flex;align-items:flex-end;
  background:var(--ink, #12161c);overflow:hidden;
}
.hero-photo img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  /* The subject sits high in these frames; anchoring the crop keeps faces and
     rooflines in shot when the viewport gets short. */
  object-position:50% 42%;
}
/* Two scrims, not one. A bottom-only gradient leaves the middle of the frame
   almost clear, which is exactly where a left-aligned lede sits — over bright
   sky or foliage it becomes unreadable. The horizontal scrim gives the text a
   ground while leaving the right of the photograph at full brightness, which
   is where the subject usually is. */
.hero-photo::after{
  content:"";position:absolute;inset:0;
  background:
    linear-gradient(90deg,rgb(18 20 24 / .82) 0%,rgb(18 20 24 / .58) 34%,rgb(18 20 24 / .12) 62%,rgb(18 20 24 / 0) 82%),
    linear-gradient(180deg,rgb(18 20 24 / .3) 0%,rgb(18 20 24 / .12) 42%,rgb(18 20 24 / .55) 100%);
}
.hero-photo .wrap{position:relative;z-index:2;padding-block:clamp(2.5rem,6vw,4.5rem)}
.hero-photo h1{
  text-shadow:0 2px 22px rgb(18 20 24 / .45);
  color:#fff;font-size:clamp(2.1rem,5.4vw,4rem);line-height:1.02;letter-spacing:-.022em;
  max-width:19ch;text-wrap:balance;
}
.hero-photo .lede{color:rgb(255 255 255 / .92);max-width:44ch;margin-top:1rem;font-size:1.02rem;text-shadow:0 1px 14px rgb(18 20 24 / .5)}
.hero-photo .hero-note{color:rgb(255 255 255 / .72);margin-top:1.1rem;font-size:.86rem}

/* The finder lifts off the photograph rather than sitting on it. */
.hero-photo .finder{
  margin-top:1.6rem;background:var(--paper, #ffffff);border-radius:calc(var(--r) + 4px);
  padding:1rem;box-shadow:0 18px 44px rgb(20 23 28 / .3);border:0;
}

/* ---- Photographic tiles ---- */
.tile-grid{display:grid;gap:1.15rem;grid-template-columns:repeat(auto-fill,minmax(15rem,1fr))}
.tile{
  position:relative;display:block;text-decoration:none;color:inherit;
  border-radius:calc(var(--r) + 4px);overflow:hidden;background:var(--sunk, #f5f7fa);
  aspect-ratio:16/10;isolation:isolate;
}
.tile img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  transition:transform .5s cubic-bezier(.2,.6,.2,1);
}
.tile::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgb(20 23 28 / 0) 34%,rgb(20 23 28 / .76) 100%);
}
.tile-body{position:absolute;left:0;right:0;bottom:0;z-index:2;padding:1rem 1.05rem}
.tile-name{display:block;color:#fff;font-family:var(--display),system-ui,sans-serif;font-weight:800;font-size:1.06rem;line-height:1.15;letter-spacing:-.005em}
.tile-meta{display:block;color:rgb(255 255 255 / .78);font-size:.76rem;margin-top:.22rem}
.tile:hover img{transform:scale(1.045)}
.tile:focus-visible{outline:2px solid var(--brand, #1a73e8);outline-offset:3px}

/* Uniform tiles, deliberately. A spanning first tile reads well only at one
   column count, and this grid reflows from four columns to two — at every
   width but the intended one it left a hole beside the large tile and an
   orphan on the last row. A clean grid of good photographs is the more
   confident thing anyway, and it is what Houzz actually does. */
.tile-grid.featured{grid-template-columns:repeat(auto-fill,minmax(16.5rem,1fr))}
@media (min-width:60rem){
  .tile-grid.featured .tile-name{font-size:1.2rem}
}

/* ---- Category page hero ---- */
.cat-hero{position:relative;min-height:clamp(220px,34vw,340px);display:flex;align-items:flex-end;background:var(--ink, #12161c);overflow:hidden}
.cat-hero img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 45%}
.cat-hero::after{
  content:"";position:absolute;inset:0;
  background:
    linear-gradient(90deg,rgb(18 20 24 / .84) 0%,rgb(18 20 24 / .6) 36%,rgb(18 20 24 / .14) 64%,rgb(18 20 24 / 0) 84%),
    linear-gradient(180deg,rgb(18 20 24 / .34) 0%,rgb(18 20 24 / .16) 40%,rgb(18 20 24 / .58) 100%);
}
.cat-hero .wrap{position:relative;z-index:2;padding-block:clamp(1.75rem,4vw,2.75rem)}
.cat-hero .crumbs{color:rgb(255 255 255 / .7)}
.cat-hero .crumbs a{color:rgb(255 255 255 / .86)}
.cat-hero h1{text-shadow:0 2px 20px rgb(18 20 24 / .45);color:#fff;font-size:clamp(1.7rem,4vw,2.7rem);line-height:1.06;letter-spacing:-.018em}
.cat-hero .lede{color:rgb(255 255 255 / .92);max-width:52ch;margin-top:.7rem;text-shadow:0 1px 14px rgb(18 20 24 / .5)}

/* ---- More air throughout ---- */
section{padding-block:clamp(2.5rem,6vw,4.25rem)}
.sec-head{max-width:56rem;margin-bottom:2rem}
.sec-head h2{font-size:clamp(1.5rem,3.2vw,2.15rem);line-height:1.1;letter-spacing:-.018em}
.sec-head .lede{margin-top:.6rem}
.eyebrow{margin-bottom:.6rem}

/* ---- Utilities the CSP forces us to name --------------------------------
 *
 * style-src is 'self' with no unsafe-inline, so a style attribute never
 * reaches the layout — it is dropped silently, which is how a missing margin
 * and a visible honeypot both shipped. Anything positional lives here. */
.subhead{margin-top:2.75rem;margin-bottom:1rem}
.subhead h3{font-size:1.02rem;letter-spacing:-.005em}
.sec-tight{padding-block:1.5rem 0}

/* ---- Hero on narrow screens ----------------------------------------------
 *
 * The horizontal scrim exists to give left-aligned copy a ground while leaving
 * the right of the photograph bright. On a phone there is no "right of the
 * frame" — the text spans the full width — so the same gradient simply turns
 * the whole picture black, which is what it was doing. Narrow screens get a
 * lighter, vertical-only scrim, and a shorter hero so the crop is not a
 * letterbox slice of whatever happens to be in the middle of the image. */
@media (max-width:47.99rem){
  .hero-photo{min-height:auto}
  .hero-photo::after{
    background:linear-gradient(180deg,rgb(18 20 24 / .52) 0%,rgb(18 20 24 / .42) 45%,rgb(18 20 24 / .68) 100%);
  }
  .hero-photo img{object-position:50% 38%}
  .hero-photo .lede{max-width:none}
  .cat-hero{min-height:auto}
  .cat-hero::after{
    background:linear-gradient(180deg,rgb(18 20 24 / .5) 0%,rgb(18 20 24 / .42) 45%,rgb(18 20 24 / .7) 100%);
  }
  .cat-hero .lede{max-width:none}
}

/* The note under the finder sits where the vertical scrim is weakest. */
.hero-photo .hero-note{text-shadow:0 1px 12px rgb(18 20 24 / .6)}

/* ---- Logo in the dashboard ---- */
.logo-row{display:flex;gap:1.25rem;align-items:flex-start;flex-wrap:wrap;margin-bottom:1.5rem}
.logo-box{
  flex:none;width:9rem;height:9rem;border:1px solid var(--rule, #e3e8ef);border-radius:var(--r);
  background:var(--paper-2, #f7f9fc);display:flex;align-items:center;justify-content:center;overflow:hidden;
}
.logo-box img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain}
.logo-empty{font-size:.78rem;color:var(--muted, #6b7684);text-align:center;padding:.5rem}
.logo-actions{flex:1;min-width:15rem}
.logo-actions h3{font-size:1rem;margin-bottom:.35rem}
.logo-actions .field-hint{margin-bottom:.75rem}

/* ---- Social row on a profile ---- */
.social-row{list-style:none;padding:0;margin:.9rem 0 0;display:flex;flex-wrap:wrap;gap:.4rem}
.social-row a{
  display:inline-block;min-height:var(--tap);line-height:calc(var(--tap) - 1.5rem);
  padding:.75rem .85rem;border:1px solid var(--rule, #e3e8ef);border-radius:999px;
  text-decoration:none;color:var(--ink-2, #39424e);font-size:.82rem;background:var(--paper, #ffffff);
}
.social-row a:hover{border-color:var(--brand, #1a73e8);background:var(--brand-tint, #e8f0fe);color:var(--brand-ink, #0b4ea2)}
.contact-addr{color:var(--muted, #6b7684);font-size:.88rem}
.profile-logo{
  width:4.5rem;height:4.5rem;object-fit:contain;background:#fff;
  border:1px solid var(--rule, #e3e8ef);border-radius:var(--r);padding:.35rem;margin-bottom:.75rem;
}

/* ---- Profile, directory layout -------------------------------------------
 *
 * Work first, identity second, contact pinned alongside. A homeowner arrives to
 * judge the photographs, so they lead — and a business that has bothered to
 * upload ten good ones should get the benefit of that before anyone reads a
 * word about them.
 */

/* ---- The gallery ---------------------------------------------------------
 *
 * A wide center frame with the neighboring photographs showing at the edges,
 * which is what tells a visitor there is more than one before they touch
 * anything. The measurements are taken from the layout this was asked to
 * match: the frame is 89% of the viewport, so about 5.5% of the previous and
 * next photograph shows either side, and the frame itself is 694 by 294.
 *
 * Two modes, one markup. As shipped it is a native horizontal scroller with
 * snap points, which swipes on a phone and needs no script at all. The script
 * then adds .is-live and the track moves under a transform instead, so the
 * arrows can advance exactly one photograph and the run can loop.
 */
.hz-gallery{position:relative;background:var(--ink, #12161c);padding-block:0;isolation:isolate}
.hz-viewport{overflow:hidden}
.hz-track{
  list-style:none;margin:0;padding:0;display:flex;
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;scrollbar-width:none;-ms-overflow-style:none;
}
.hz-track::-webkit-scrollbar{display:none}
.hz-slide{flex:0 0 100%;scroll-snap-align:center;padding-inline:2px}
/* ---- The frame ------------------------------------------------------------
 *
 * 3:2, and the photograph is contained inside it rather than cropped to fill.
 *
 * A frame that crops is fine when every photograph is the same shape. Real
 * listings are not: the four on the first published profile are 1.00, 1.33,
 * 1.43 and 2.02, and the widest of them is a before/after composite. Every
 * crop tested cut the word BEFORE clean off it — which is to say it destroyed
 * the only thing that photograph was for.
 *
 * So nothing is cropped, and the empty space either side is filled with the
 * photograph's own colors, blurred. The frame stays one size on every card
 * and every listing; what varies is how much of it the photograph covers.
 *
 * 3:2 because it is the middle of what businesses actually upload and it is
 * what a camera shoots. Upload 3:2 and it fills the frame exactly.
 */
.hz-slide figure{margin:0;position:relative;aspect-ratio:3/2;overflow:hidden;background:#0d1117}
.hz-slide img{position:absolute;inset:0;width:100%;height:100%;display:block}
.hz-shot{object-fit:contain;z-index:1}
/* Overscanned and blurred, so the blur has pixels to work with at the edges
   instead of smearing the frame border inwards. The source is the 200px
   rendition: at a 22px blur radius nothing finer than that survives, so asking
   for more detail buys a bigger download and an identical picture. */
.hz-fill{
  inset:-6% !important;width:112% !important;height:112% !important;
  object-fit:cover;filter:blur(22px) brightness(.55);z-index:0;
}
@media (prefers-reduced-motion:no-preference){
  .hz-fill{transform:scale(1.02)}
}
.hz-slide figcaption{
  position:absolute;inset:auto 0 0 0;z-index:2;padding:2.5rem .95rem .7rem;color:#fff;font-size:.85rem;
  background:linear-gradient(to top,rgb(0 0 0 / .62),rgb(0 0 0 / 0));
}

@media (min-width:60rem){
  .hz-slide{flex-basis:89%}
  .hz-count{right:calc(5.5% + .9rem)}
  /* Half the leftover, so the first and last photographs can still sit in the
     center of the frame rather than jamming against the edge. */
  .hz-track{padding-inline:5.5%}

  /* One photograph has no neighbors, so the 11% held back for them is just a
     band of dark ground down one side. The markup already knew this case —
     .hz-single has been on the element since the carousel was written — but
     nothing was reading it. */
  .hz-single .hz-slide{flex-basis:100%}
  .hz-single .hz-track{padding-inline:0}
  .hz-single .hz-count{right:.9rem}
}

.hz-gallery.is-live .hz-track{
  overflow:visible;scroll-snap-type:none;padding-inline:0;
  transition:transform .5s;will-change:transform;
}
.hz-gallery.is-live .hz-slide figure{cursor:zoom-in}
@media (prefers-reduced-motion:reduce){
  .hz-gallery.is-live .hz-track{transition:none}
}

/* Full-height hit areas at the edges with the control itself a small disc
   centered in them: a 46px target carrying a 34px button, so it is thumb-sized
   on a touch screen without the disc looking clumsy on a desktop. */
.hz-arrow{
  display:none;position:absolute;top:0;bottom:0;width:46px;z-index:2;
  border:0;background:none;padding:0;cursor:pointer;align-items:center;justify-content:center;
}
.hz-gallery.is-live .hz-arrow{display:flex}
.hz-prev{left:0}
.hz-next{right:0}
.hz-arrow>span{
  display:grid;place-content:center;width:34px;height:34px;border-radius:50%;
  background:rgb(255 255 255 / .92);color:#3c4043;box-shadow:0 1px 5px rgb(0 0 0 / .35);
  transition:background .15s,transform .15s;
}
.hz-arrow:hover>span{background:#fff;transform:scale(1.07)}
.hz-arrow:focus-visible>span{outline:3px solid var(--brand, #1a73e8);outline-offset:2px}
.hz-arrow svg{fill:currentColor}

.hz-count{
  position:absolute;right:.9rem;bottom:.8rem;z-index:2;margin:0;
  /* Inset to the center frame rather than the viewport edge, or it sits on top
     of the next photograph peeking through and reads as part of that one. */
  background:rgb(0 0 0 / .55);color:#fff;border-radius:999px;
  padding:.24rem .62rem;font-size:.78rem;letter-spacing:.02em;
}
.hz-gallery:not(.is-live) .hz-count{display:none}

/* The lightbox is a native dialog, so Escape, the backdrop and the focus trap
   all come free rather than being re-implemented badly. */
.hz-box{
  border:0;padding:0;width:100%;height:100%;max-width:100vw;max-height:100vh;
  background:#0b0d10;color:#e7eaee;overflow:hidden;
}
.hz-box::backdrop{background:rgb(0 0 0 / .88)}
/* Column flex rather than a grid: place-items on a full-height grid spreads
   the two rows apart, which stranded the caption at the bottom of the screen
   instead of under the photograph it describes. */
.hz-box figure{
  margin:0;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:.65rem;width:100%;height:100%;padding:1rem;
}
.hz-box img{max-width:94vw;max-height:80vh;width:auto;height:auto;object-fit:contain;display:block}
.hz-box figcaption{padding:0 1rem;text-align:center;font-size:.9rem;color:#c9ced6}
.hz-box-btn{
  position:absolute;z-index:3;display:grid;place-content:center;
  width:42px;height:42px;border-radius:50%;border:0;cursor:pointer;
  background:rgb(255 255 255 / .14);color:#fff;
}
.hz-box-btn:hover{background:rgb(255 255 255 / .26)}
.hz-box-btn svg{fill:currentColor}
.hz-box-close{top:1rem;right:1rem}
.hz-box-prev{left:1rem;top:50%;margin-top:-21px}
.hz-box-next{right:1rem;top:50%;margin-top:-21px}

/* Not a band across the page any more: it is the top of the left column, the
   way the layout this matches has it — carousel, then identity, then the tab
   bar, with the inquiry form standing beside all three from the very top. */
.profile-head{padding-block:1.15rem 0}
.profile-id{display:flex;gap:1.15rem;align-items:flex-start;margin:.9rem 0 1.1rem}
.profile-logo{
  flex:none;width:5.5rem;height:5.5rem;object-fit:contain;background:#fff;
  border:1px solid var(--rule, #e3e8ef);border-radius:calc(var(--r) + 2px);padding:.4rem;
}
.profile-logo-fallback{
  display:flex;align-items:center;justify-content:center;font-family:var(--display),sans-serif;
  font-weight:800;font-size:1.7rem;color:var(--muted, #6b7684);background:var(--sunk, #f5f7fa);
}
.profile-id-text{min-width:0}
.profile-id h1{font-size:clamp(1.5rem,3.6vw,2.3rem);line-height:1.08;letter-spacing:-.018em}
.profile-sub{color:var(--muted, #6b7684);font-size:.95rem;margin-top:.25rem}
.profile-badges{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:.7rem}

/* Section jump-links, in the manner of a directory profile. They are anchors
   rather than tabs — no JavaScript, and the sections stay readable in order for
   anyone who ignores them. */
.profile-tabs{display:flex;gap:.15rem;overflow-x:auto;border-bottom:1px solid var(--rule, #e3e8ef);margin-top:.25rem}
.profile-tabs a{
  flex:none;padding:.85rem 1rem;text-decoration:none;color:var(--ink-2, #39424e);
  font-size:.9rem;font-family:'bodyb',sans-serif;border-bottom:2px solid transparent;
}
.profile-tabs a{border-bottom:2px solid transparent;margin-bottom:-1px}
.profile-tabs a:hover{color:var(--brand-ink, #0b4ea2);border-bottom-color:var(--brand, #1a73e8)}
/* Not the gallery. It is a section too, and section padding on it is a band of
   dark ground above the photograph rather than space between two headings. */
.profile-main>section:not(.hz-gallery){padding-top:1.75rem}

.profile-cols{display:grid;gap:2.25rem;grid-template-columns:1fr;padding-block:.75rem 2rem}
.profile-main{min-width:0}
/* Full-bleed on a phone. The column is the page there, and a photograph inset
   by the page gutter looks like a mistake rather than a margin. */
@media (max-width:61.99rem){
  .profile-main>.hz-gallery{margin-inline:calc(var(--pad) * -1)}
}
.profile-main>section{margin-bottom:2.25rem;scroll-margin-top:1.5rem}
.profile-main>section:last-child{margin-bottom:0}
.profile-main h2{font-size:1.2rem;margin-bottom:.9rem;letter-spacing:-.01em}
@media (min-width:62rem){
  .profile-cols{grid-template-columns:minmax(0,1fr) 22rem}
  /* Pinned so the inquiry form is reachable from anywhere in a long profile —
     which is the whole point of the right-hand column. */
  .profile-aside{position:sticky;top:1.25rem;align-self:start}
}

/* ---- Profile sections ----------------------------------------------------- */

/* Icons sit inside the fact badges rather than beside them, so a badge stays
   one object when the row wraps. */
.profile-badges .badge{display:inline-flex;align-items:center;gap:.35rem}
.profile-badges .badge svg{fill:currentColor;opacity:.7}

.profile-actions{display:flex;flex-wrap:wrap;gap:.5rem;margin:0 0 1.15rem}
.act{
  display:inline-flex;align-items:center;justify-content:center;gap:.45rem;
  min-height:var(--tap);padding:.6rem 1.05rem;border:1px solid var(--rule, #e3e8ef);
  border-radius:999px;background:var(--paper, #ffffff);color:var(--ink-2, #39424e);
  font-family:'bodyb',sans-serif;font-size:.88rem;text-decoration:none;cursor:pointer;
}
.act:hover{border-color:var(--brand, #1a73e8);color:var(--brand-ink, #0b4ea2);background:var(--brand-tint, #e8f0fe)}
.act svg{fill:currentColor}
.act-cta{background:var(--brand, #1a73e8);border-color:var(--brand, #1a73e8);color:#fff}
.act-cta:hover{background:var(--brand-ink, #0b4ea2);border-color:var(--brand-ink, #0b4ea2);color:#fff}
.act-save[aria-pressed="true"]{border-color:var(--brand, #1a73e8);color:var(--brand-ink, #0b4ea2);background:var(--brand-tint, #e8f0fe)}
.act-saved[hidden]{display:none}

.profile-tagline{font-family:'bodyb',sans-serif;color:var(--ink, #12161c);margin-bottom:.65rem}
/* Clamped by the script, never by the markup: with no JavaScript the whole
   description is simply there, which is also the version a crawler reads. */
.readmore.is-clamped{
  display:-webkit-box;-webkit-line-clamp:6;-webkit-box-orient:vertical;overflow:hidden;
}
.rm-toggle{
  margin-top:.55rem;padding:.35rem 0;border:0;background:none;cursor:pointer;
  color:var(--brand-ink, #0b4ea2);font-family:'bodyb',sans-serif;font-size:.88rem;
}
.rm-toggle:hover{text-decoration:underline}

.biz-sub{font-size:.78rem;text-transform:uppercase;letter-spacing:.05em;color:var(--muted, #6b7684);margin-top:1.3rem}
.biz-details{margin:0;border-top:1px solid var(--rule, #e3e8ef)}
.biz-row{display:grid;gap:.15rem;padding:.8rem 0;border-bottom:1px solid var(--rule, #e3e8ef)}
.biz-row dt{color:var(--muted, #6b7684);font-size:.78rem;text-transform:uppercase;letter-spacing:.05em}
.biz-row dd{margin:0}
.biz-row dd .social-row{margin-top:.35rem}
@media (min-width:40rem){
  .biz-row{grid-template-columns:11rem minmax(0,1fr);gap:1rem;align-items:baseline}
}

.proj-grid{display:grid;gap:1.1rem;grid-template-columns:repeat(auto-fill,minmax(14.5rem,1fr))}
.proj{margin:0;border:1px solid var(--rule, #e3e8ef);border-radius:var(--r);overflow:hidden;background:var(--paper, #ffffff)}
.proj-frame{
  display:block;position:relative;aspect-ratio:3/2;overflow:hidden;background:#0d1117;
}
.proj-frame img{position:absolute;inset:0;width:100%;height:100%;display:block}
.proj figcaption{display:grid;gap:.15rem;padding:.7rem .8rem .85rem}
.proj-title{font-family:'bodyb',sans-serif;font-size:.92rem;color:var(--ink, #12161c)}
.proj-where{font-size:.82rem;color:var(--muted, #6b7684)}

.cred-list{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:.5rem}
.cred-list li{
  display:inline-flex;align-items:center;gap:.4rem;padding:.5rem .9rem;
  border:1px solid var(--rule, #e3e8ef);border-radius:999px;background:var(--paper, #ffffff);font-size:.88rem;
}
.cred-list svg{fill:var(--brand, #1a73e8)}
.fine{color:var(--muted, #6b7684);font-size:.84rem;margin-top:.8rem}

/* The saved list lives in the visitor's own browser, so the page ships empty
   and the script fills it. Both states are styled here rather than injected —
   the CSP has no unsafe-inline and a style attribute would be dropped. */
.saved-grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fill,minmax(16rem,1fr))}
.saved-card{border:1px solid var(--rule, #e3e8ef);border-radius:var(--r);padding:1rem;background:var(--paper, #ffffff)}
.saved-card h3{font-size:1rem;margin:0 0 .2rem}
.saved-card h3 a{text-decoration:none;color:var(--ink, #12161c)}
.saved-card h3 a:hover{color:var(--brand-ink, #0b4ea2)}
.saved-card p{color:var(--muted, #6b7684);font-size:.86rem;margin:0 0 .7rem}
.saved-drop{border:0;background:none;padding:.3rem 0;cursor:pointer;color:var(--muted, #6b7684);font-size:.84rem}
.saved-drop:hover{color:var(--brand-ink, #0b4ea2);text-decoration:underline}
.saved-empty{color:var(--muted, #6b7684)}

/* ---- The pro's FAQ editor -------------------------------------------------
 *
 * Pre-filled with the questions already on their profile, so this is editing
 * rather than authoring — which is the difference between an FAQ that gets
 * written and an empty box nobody fills in.
 */
.dash-faqs{margin-top:2.5rem}
.faq-edit{
  border:1px solid var(--rule, #e3e8ef);border-radius:var(--r);
  padding:.9rem 1rem 1rem;margin:0 0 .85rem;background:var(--paper, #ffffff);
}
.faq-edit legend{
  padding:0 .4rem;font-size:.74rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted, #6b7684);
}
.faq-edit .field{margin-bottom:.6rem}
.faq-edit .field:last-child{margin-bottom:0}
.faq-edit textarea{resize:vertical}

/* ---- Trade picture as the fallback ---------------------------------------
 *
 * A listing with no photographs used to show two letters in a gray box, which
 * told a homeowner nothing except that this business was emptier than the one
 * beside it. The trade's own picture — the same one the homepage category
 * tiles use — says what the business does instead.
 */
.pro-shot-trade{width:100%;height:100%;object-fit:cover;display:block}

/* Cropped square rather than contained, because this is a photograph filling
   an avatar slot, not a logo that needs its whitespace respected. */
.profile-logo-trade{object-fit:cover;padding:0;background:var(--sunk, #f5f7fa)}

/* Said once, quietly, on the slide itself. This is the portfolio position, so
   without it a homeowner would reasonably read a category photograph as this
   business's own work — and the business has not claimed it is. */
.hz-stock{
  position:absolute;left:0;right:0;bottom:0;z-index:2;margin:0;
  padding:2.25rem .95rem .65rem;color:rgb(255 255 255 / .82);
  font-size:.78rem;letter-spacing:.01em;
  background:linear-gradient(to top,rgb(0 0 0 / .62),rgb(0 0 0 / 0));
}
/* ---- Three sources, three grounds -----------------------------------------
 *
 * The page carries three measurements of the same site that will never agree,
 * and a reader who assumes they should concludes the site lost traffic. So each
 * source gets its own ground color and its own explanation of what it can and
 * cannot see. The color is doing real work here: it is the difference between
 * "these numbers disagree" and "these are different instruments".
 *
 * Neutral for our own log, because that is the count we quote. Green for
 * Search Console, blue for Analytics — Google's own product colors, and
 * neither is the site accent, which means "ours".
 */
.src-head{
  font-size:.78rem;text-transform:uppercase;letter-spacing:.09em;
  font-weight:700;margin:0 0 .3rem;
}
.src-lede{font-size:.86rem;line-height:1.55;margin:0 0 1rem;max-width:62rem}
.src-lede strong{font-weight:700}

.src-edge{background:#f4f6f8;border-top-color:#5b6673}
.src-edge .src-head{color:#3c4854}
.src-edge .src-lede{color:#4a5663}

.src-gsc{background:#f0f8f2;border-top-color:#1e8e3e}
.src-gsc .src-head{color:#10652a}
.src-gsc .src-lede{color:#2f6b42}
.src-gsc .panel{border-color:#cfe6d6}

.src-ga{background:#f2f7fd;border-top-color:#1a73e8}
.src-ga .src-head{color:#12467f}
.src-ga .src-lede{color:#3d5878}
.src-ga .panel{border-color:#c9dcf4}

/* The caveat that stops the three sections being read as one number measured
   three times. Sits inside its section so it inherits the ground. */
.src-note{
  margin:0 0 1rem;padding:.8rem .95rem;border-radius:3px;
  background:rgba(255,255,255,.75);font-size:.82rem;line-height:1.55;
}
.src-note ul{margin:.45rem 0 0;padding-left:1.1rem}
.src-note li{margin-bottom:.28rem}
.src-gsc .src-note{color:#2f6b42}
.src-ga .src-note{color:#3d5878}
.src-edge .src-note{color:#4a5663}

/* ---- Panels in columns ----------------------------------------------------
 *
 * Seventeen full-width panels ran to several screens and made the three
 * sections feel like three pages. auto-fit means the same markup is one column
 * on a phone and three on a desktop without a breakpoint per layout.
 *
 * Charts and the wide tables opt out: a sparkline squeezed into a third of the
 * width is smaller without being more readable, which is the wrong trade.
 */
.panels{display:grid;gap:.85rem;grid-template-columns:1fr;align-items:start}
.panels .panel{margin-bottom:0}
@media (min-width:56rem){
  .panels{grid-template-columns:repeat(auto-fit,minmax(20rem,1fr))}
  .panels .panel-wide{grid-column:1 / -1}
}
.panels .panel h2{font-size:.92rem;margin-bottom:.6rem}
.panels .panel .pg-sub{font-size:.78rem;margin-bottom:.6rem}

/* Air between the sections, so the color change reads as a boundary rather
   than as a panel that happens to be tinted. */
/* The nav is sticky, so an anchor must not land under it. */
.src-nav{
  position:sticky;top:0;z-index:5;display:flex;flex-wrap:wrap;gap:.4rem;
  margin:0 -1rem 1.25rem;padding:.7rem 1rem;
  background:rgba(255,255,255,.94);backdrop-filter:saturate(140%) blur(8px);
  border-bottom:1px solid var(--rule, #e3e8ef);
}
.src-nav a{
  font-size:.76rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  text-decoration:none;padding:.35rem .7rem;border-radius:999px;
  border:1px solid var(--rule, #e3e8ef);color:var(--ink-2,#39424e);background:#fff;
}
.src-nav a:hover{border-color:currentColor}
.src-nav a:nth-child(1):hover{color:#3c4854}
.src-nav a:nth-child(2){color:#10652a;border-color:#cfe6d6}
.src-nav a:nth-child(3){color:#12467f;border-color:#c9dcf4}

.why{margin:0 0 .9rem}
.why summary{display:inline-block;cursor:pointer;list-style:none;font-size:.75rem;font-weight:600;
  color:var(--muted, #6b7684);border-bottom:1px dotted currentColor;padding:.1rem 0}
.why summary::-webkit-details-marker{display:none}
.why[open] summary{margin-bottom:.5rem}
.why .src-note{margin:0}

/* ---- Overflow -------------------------------------------------------------
 *
 * A grid item defaults to min-width:auto, which means it refuses to shrink
 * below the intrinsic width of its widest child. A five-column table inside a
 * 19rem column therefore pushes the panel wider than its track, clipping the
 * last column and shoving the next panel off the edge — which is exactly what
 * it did.
 *
 * min-width:0 lets the track win; overflow-x:auto gives the table somewhere to
 * go instead of being cut off. Both are needed: the first alone would squash
 * the table, the second alone would never trigger.
 */
.panels > *,.panels .panel{min-width:0}
.panel{overflow-x:auto;overflow-y:visible}
.panel table{width:100%;border-collapse:collapse}
.panel td,.panel th{overflow-wrap:anywhere}

/* Long URLs and search phrases are the usual culprit: one unbroken string is
   as wide as it is, and no amount of column sizing helps. */
.panel .bar-label,.panel .tbl td:first-child{overflow-wrap:anywhere;word-break:break-word}

/* Nothing on the page may push the document sideways. */
html,body{max-width:100%;overflow-x:hidden}

/* ---- Overflow, corrected --------------------------------------------------
 *
 * overflow-wrap:anywhere on every cell broke headings mid-word ("Visit s",
 * "Conversi ons"). It belongs only on the cells that actually carry unbreakable
 * strings — URLs and search phrases in the first column. Headings and numbers
 * wrap at word boundaries or not at all.
 */
.panel th{overflow-wrap:normal;word-break:normal;hyphens:none;white-space:normal}
.panel td{overflow-wrap:normal}
.panel td:first-child,.panel .bar-label{overflow-wrap:anywhere}
.panel td.n,.panel th.n{white-space:nowrap;text-align:right}

/* auto-fill keeps empty tracks, which is where the gap on the right came from.
   auto-fit collapses them, so two panels share the row instead of two panels
   and a hole. */
@media (min-width:56rem){
  .panels{grid-template-columns:repeat(auto-fit,minmax(19rem,1fr))}
}

/* The chart is drawn to its container, so the container has to be the thing
   that is bounded. */
.panel-wide{overflow:hidden}
.panel-wide #chart,.panel-wide .spark{max-width:100%;overflow-x:auto}

/* ---- Track width, finally -------------------------------------------------
 *
 * A panel spanning 1 / -1 fixes the track count, so auto-fit cannot collapse
 * the unused one — which is where the empty third column came from. Wider
 * tracks mean the row is full at two panels on a normal screen, and three only
 * on a genuinely wide one. Fewer, wider panels also read better: these are
 * tables, not tiles.
 */
@media (min-width:56rem){
  .panels{grid-template-columns:repeat(auto-fit,minmax(24rem,1fr))}
}
@media (min-width:100rem){
  .panels{grid-template-columns:repeat(auto-fit,minmax(21rem,1fr))}
}


/* ---- Masonry, and air between the sections --------------------------------
 *
 * Grid gives every item in a row the height of the tallest, so a five-row table
 * beside a two-row one leaves a hole under the short one. CSS columns pack
 * bottom-up instead — true masonry, no gaps, and no JavaScript. It is also the
 * only version of this that degrades sanely: one column on a phone, and the
 * browser decides the rest.
 *
 * The trade is reading order: columns flow top-to-bottom then across, so panel
 * two sits under panel one rather than beside it. These panels are independent
 * readings, so that costs nothing.
 */
.panels{
  display:block;
  columns:1;
  column-gap:1rem;
}
@media (min-width:56rem){ .panels{columns:2} }
@media (min-width:92rem){ .panels{columns:3} }

.panels > *{
  break-inside:avoid;
  -webkit-column-break-inside:avoid;
  page-break-inside:avoid;
  display:block;
  margin:0 0 1rem;
  width:100%;
}

/* Charts want the full width, and column-span resets the flow around them. */
.panels > .panel-wide{
  column-span:all;
  -webkit-column-span:all;
  margin-bottom:1.15rem;
}

/* ---- Section separation ---------------------------------------------------
 *
 * The bands were landing directly on the panel above them, so the color change
 * read as a rendering fault rather than a boundary. Each section is now an
 * inset block with its own ground and real space around it.
 */
.src-head{margin:0 0 .35rem}
.src-lede{margin:0 0 1rem;max-width:62ch}
.why{margin:0 0 1.15rem}


/* ---- Section spacing, actually ---------------------------------------------
 *
 * `.src + .src` only works when the sections are adjacent siblings, and they
 * are not — a "show more" details block sits between them, so the rule never
 * matched and the bands kept landing on the panel above. Margin on the section
 * itself does not care what is between them.
 */
.src:last-of-type{margin-bottom:1rem}

/* Panels have to read as separate cards against a near-white ground, or a
   column of them merges into one sheet of text. */
.panels > .panel{
  background:#fff;
  border:1px solid #dde3ea;
  border-radius:3px;
  box-shadow:0 1px 2px rgba(16,24,40,.04);
}

/* ---- Sections: one definition, deliberately last --------------------------
 *
 * Replaces five overlapping .src rules that accumulated across patches. Each
 * set part of the spacing and partly overrode the one before, so the result was
 * whatever the cascade landed on rather than anything chosen.
 */
.src{
  padding:1.75rem 1.35rem 1.6rem;
  border-radius:4px;
  margin:0 0 3rem;
  scroll-margin-top:4.5rem;
}
.src:last-of-type{margin-bottom:1.5rem}
.src-head{margin:0 0 .35rem}
.src-lede{margin:0 0 1rem;max-width:62ch}
.why{margin:0 0 1.15rem}

/* ---- Analytics cards, in Google's own shape -------------------------------
 *
 * Metric-by-dimension, a magnitude bar under each label, and the change against
 * the same period before it with a colored arrow. The bar is what makes a
 * column of numbers readable at a glance, and the arrow is what turns a number
 * into news.
 *
 * Red for down and green for up is Google's convention and clients already read
 * it that way — but it is only applied where a direction is genuinely good or
 * bad. Nothing is colored on rows where a rise is neutral.
 */
.gcard-h{
  display:flex;align-items:baseline;justify-content:space-between;gap:.5rem;
  font-size:.68rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--muted,#6b7684);border-bottom:1px solid var(--rule,#e2e6ec);
  padding-bottom:.35rem;margin-bottom:.15rem;
}
.grow{padding:.5rem 0 .45rem;border-bottom:1px solid rgba(0,0,0,.045)}
.grow:last-child{border-bottom:0}
.grow-top{display:flex;align-items:baseline;justify-content:space-between;gap:.6rem}
.grow-l{font-size:.88rem;color:var(--ink,#12161c);overflow-wrap:anywhere;min-width:0}
.grow-r{display:flex;align-items:baseline;gap:.55rem;white-space:nowrap;flex:none}
.grow-v{font-size:.88rem;font-variant-numeric:tabular-nums}
.grow-d{font-size:.75rem;font-variant-numeric:tabular-nums}
.grow-d.up{color:#137333}
.grow-d.down{color:#c5221f}
.grow-d.flat{color:var(--muted,#6b7684)}
.gbar{height:3px;border-radius:2px;background:#e8eef6;margin-top:.35rem;overflow:hidden}
.gbar span{display:block;height:100%;background:#1a73e8;border-radius:2px}
.src-gsc .gbar span{background:#1e8e3e}
.src-edge .gbar span{background:#5b6b7d}

/* Labels truncate rather than wrap or push. Google's own cards do the same —
   "Page title and scree…" — because a long page title otherwise forces the
   value and its change past the card edge, which is what it was doing. */
.grow-l{
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  flex:1 1 auto;min-width:0;
}
.grow-top{flex-wrap:nowrap}
.gcard-h span:last-child{flex:none}
/* The bar spans the row, so it is measured against the card, not the label. */
.gbar{width:100%}

/* Now that labels truncate, the panel no longer needs to be a scroll container
   — and being one was itself the clipping: inside a CSS column, an overflow
   box establishes its own formatting context and paints past the column edge.
   Tables keep their own scroller; cards do not need one. */
.panel{overflow:visible}
.panel table{display:block;overflow-x:auto;max-width:100%}
.panel .grow,.panel .gcard-h{max-width:100%}

/* (The grid-not-columns block that used to sit here is gone. It was written
   when a multi-column panel overlapped its neighbor; the actual cause was a
   missing display:inline-block, fixed in the masonry block below. Its
   `columns:auto !important` was silently disabling that masonry.) */


/* ---- Charts ---------------------------------------------------------------
 *
 * Sparkline, donut and paired bars, sized in CSS so the SVG scales with the
 * panel instead of needing a resize handler.
 */
.stat-lead{padding:.15rem 0 .5rem}
.stat-lead-k{font-size:.78rem;color:var(--muted,#6b7684);margin:0 0 .15rem}
.stat-lead-r{display:flex;align-items:center;gap:.55rem}
.stat-lead-n{font-size:1.75rem;line-height:1.05;font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.stat-lead-s{flex:1 1 auto;min-width:0;max-width:11rem}
.spark-svg{display:block;width:100%;height:28px}

.donut{position:relative;display:grid;place-items:center;padding:.35rem 0 .1rem}
.donut svg{width:104px;height:104px;display:block}
.donut-n{position:absolute;top:44%;transform:translateY(-50%);margin:0;font-size:1.05rem;font-weight:600;font-variant-numeric:tabular-nums}
.donut-k{margin:.35rem 0 0;font-size:.76rem;color:var(--muted,#6b7684)}

.pbars{display:grid;gap:.55rem;padding-top:.2rem}
.pbar{display:grid;grid-template-columns:minmax(0,7.5rem) 1fr auto;gap:.5rem;align-items:center}
.pbar-l{font-size:.8rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pbar-t{display:grid;gap:2px;min-width:0}
.pbar-t i{display:block;height:7px;border-radius:2px;background:#1a73e8}
.pbar-t i.prev{background:repeating-linear-gradient(45deg,#b9cdea 0 4px,#dbe6f5 4px 8px)}
.pbar-n{font-size:.8rem;font-variant-numeric:tabular-nums;color:var(--ink-2,#39424e)}

/* ---- Masonry, 20px, without the overlap -----------------------------------
 *
 * CSS columns is the only masonry that needs no JavaScript, and it overlapped
 * last time because a panel came out wider than its column. display:inline-block
 * with width:100% is what pins a multi-column child to its column box — the
 * missing piece, not the technique.
 *
 * Full-width panels cannot span columns in multi-col, so they live outside the
 * masonry container as their own block.
 */
.panels{
  display:block;
  column-gap:20px;
  column-count:1;
}
.panels > .panel{
  display:inline-block;
  width:100%;
  margin:0 0 20px;
  break-inside:avoid;
  -webkit-column-break-inside:avoid;
  page-break-inside:avoid;
}
@media (min-width:56rem){ .panels{column-count:2} }
@media (min-width:104rem){ .panels{column-count:3} }

/* Charts and anything explicitly full width sit above the masonry rather than
   inside it, because a multi-column child cannot span its columns. */
.panels-wide{display:grid;gap:20px;margin-bottom:20px}
.panels-wide > .panel{margin:0}

.src{padding:20px}
.src + .src{margin-top:20px}
.src .panels{margin-top:0}

/* ---- The actual cause of the overlap --------------------------------------
 *
 * Measured: a 2-column container 1000px wide with a 20px gap gives 490px
 * columns, and the panels came out 526 — 490 plus 16.8px padding either side
 * plus a 1px border. They were content-box, so width:100% meant "490 of
 * content, then add my padding", and every panel was 36px wider than the
 * column it lived in.
 *
 * That is what made columns look like the wrong tool. It was not; this line
 * was missing. It applies to grid and flex the same way, so it would have
 * come back in any layout.
 */
.panel,.panels > .panel,.panels-wide > .panel{box-sizing:border-box}
.src,.panels,.panels-wide{box-sizing:border-box}

/* The three Analytics headline cards sit in a row on the server-rendered
   dashboard, matching the grid the client-side dashboards use. */
.stat-lead-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:20px;margin-top:1rem}

/* The row cap needs both halves. The JS has always added .row-hidden; without
   this rule nothing was hidden, so every table rendered all of its rows and the
   "Show all" button toggled a class with no effect. One 26-row table then ran
   1482px tall — taller than the balanced height of its own section — and CSS
   columns packed everything else into a single neighboring column and left the
   third empty. */
tr.row-hidden,li.row-hidden{display:none}
.rows-more{
  margin-top:.6rem;background:none;border:0;padding:.2rem 0;cursor:pointer;
  font:inherit;font-size:.76rem;font-weight:600;color:var(--ink-2,#39424e);
  border-bottom:1px dotted currentColor;
}
.rows-more:hover{color:var(--accent,#b4451f)}

/* Explicit columns. The column-count rules above remain the no-JS fallback;
   once the panels are dealt, the container is a grid of real columns and the
   count is whatever was actually used, never a balancing accident. */
.panels.is-dealt{
  column-count:auto;columns:auto;
  display:grid;grid-template-columns:repeat(var(--pcols,1),minmax(0,1fr));
  gap:20px;align-items:start;
}
.panels.is-dealt > .pcol{display:flex;flex-direction:column;gap:20px;min-width:0}
.panels.is-dealt > .pcol > .panel{width:100%;margin:0;break-inside:auto}

/* Hours of the day, and the crawler list. Rendered server-side on this
   dashboard, so the shapes are markup rather than a chart library. */
.hours{display:flex;align-items:flex-end;gap:2px;height:64px;margin:.6rem 0 .25rem}
.hours .hbar{flex:1;display:flex;align-items:flex-end;height:100%}
.hours .hbar > i{display:block;width:100%;background:#1a73e8;border-radius:2px 2px 0 0;opacity:.85}
.hours-x{display:flex;justify-content:space-between;font-size:.68rem;color:var(--muted,#6b7684)}
.kv{display:flex;justify-content:space-between;gap:.75rem;padding:.45rem 0;
    border-bottom:1px solid rgba(0,0,0,.045);font-size:.88rem}
.kv:last-child{border-bottom:0}
.kv small{color:var(--muted,#6b7684);font-size:.72rem;margin-left:.35rem}

/* The bar elements get their SIZE from the shared .w*/.h* ladder — this page
   serves style-src 'self', so an inline style attribute is dropped and the bar
   renders as nothing. Color and base geometry live here. */
.pbar-t > i{display:block;height:100%;background:#1a73e8;border-radius:2px}
.pbar-t > i.prev{background:repeating-linear-gradient(45deg,#c3d3ef,#c3d3ef 3px,transparent 3px,transparent 6px);margin-top:2px;height:4px}
.gbar > span{display:block;height:100%;background:#1a73e8;border-radius:2px}

/* ---- Profile editor: numbered, colored steps ------------------------------
 *
 * The pro side was one long grey form, and new businesses were getting lost in
 * it: no sense of how much was left, the publish switch buried in the middle,
 * and services that only appeared after a save nobody knew to make. Each
 * section is now a numbered step in its own color, with a state chip that
 * says done / to do / after you save, a tracker at the top that names the next
 * step, and a dark finish card at the bottom whose only job is the button that
 * makes the profile public.
 *
 * Colors are set once per step number, on the section and on its tracker tile
 * alike, so the tile a pro taps at the top matches the card they land on. */
.step-1{--sc:#1B6EF3;--st:#EEF4FE;--sb:#BFD5FB}
.step-2{--sc:#15803D;--st:#ECFDF3;--sb:#A7F3D0}
.step-3{--sc:#B45309;--st:#FFF7E6;--sb:#FCD9A0}
.step-4{--sc:#7C3AED;--st:#F3EEFF;--sb:#D9CCFB}
.step-5{--sc:#0F766E;--st:#E6FAF7;--sb:#A6E8DC}
.step-6{--sc:#C2410C;--st:#FFF1EA;--sb:#FDBE9F}
.step-7{--sc:#BE185D;--st:#FDEEF6;--sb:#F9B8D4}
.step-8{--sc:#14171C;--st:#1F242C;--sb:#14171C}

.dash{padding-block:clamp(1.5rem,4vw,2.5rem) clamp(3rem,6vw,4.5rem)}
.dash-head .lede a{font-weight:700}

.flash{padding:.8rem 1rem;border-radius:var(--r);font-weight:600;margin:0 0 1rem;border:1px solid transparent;line-height:1.5}
.flash-ok{background:#ECFDF3;color:var(--ok, #15803d);border-color:#A7F3D0}
.flash-err{background:#FEF2F2;color:var(--err, #b91c1c);border-color:#FCA5A5}
.flash-warn{background:#FFF7E6;color:#92400E;border-color:#FCD34D}
.notice-verify{border-left-color:#B45309;background:#FFF7E6;border-color:#FDE68A;margin:0 0 1.25rem}

/* The tracker. */
.progress{border:1px solid var(--rule, #e3e8ef);border-radius:calc(var(--r) + 4px);padding:1rem;background:var(--paper, #ffffff);box-shadow:0 10px 30px rgb(20 23 28 / .05);margin:0 0 1.5rem}
.progress-sum{display:flex;flex-wrap:wrap;gap:.75rem 1rem;align-items:center;justify-content:space-between;margin-bottom:.9rem}
.progress-sum p{margin:0;color:var(--ink-2, #39424e);max-width:60ch}
.progress-sum strong{color:var(--ink, #12161c)}
.progress-list{list-style:none;margin:0;padding:0;display:grid;gap:.5rem;grid-template-columns:repeat(auto-fill,minmax(10.5rem,1fr))}
.p-item a{
  display:grid;grid-template-columns:auto 1fr;column-gap:.6rem;align-items:center;
  padding:.5rem .7rem;border:1px solid var(--sb);border-radius:var(--r);
  text-decoration:none;color:var(--ink, #12161c);background:var(--paper, #ffffff);min-height:var(--tap);
}
.p-item a:hover{background:var(--st)}
.p-num{
  grid-row:1/3;width:1.9rem;height:1.9rem;border-radius:999px;display:grid;place-content:center;
  background:var(--sc);color:#fff;font-family:var(--display),system-ui,sans-serif;font-weight:800;font-size:.9rem;flex:none;
}
.p-title{font-weight:700;font-size:.86rem;line-height:1.2}
.p-state{font-size:.74rem;color:var(--muted, #6b7684)}
.p-item.st-todo .p-state{color:var(--sc);font-weight:700}
.p-item.st-done .p-state,.p-item.st-live .p-state{color:var(--ok, #15803d);font-weight:700}
.p-item.st-done .p-state::before,.p-item.st-live .p-state::before{content:"✓ "}
.p-item.st-locked a{opacity:.6}
.p-item.step-8 a{background:var(--ink, #12161c);color:#fff;border-color:var(--ink, #12161c)}
.p-item.step-8 .p-num{background:#fff;color:var(--ink, #12161c)}
.p-item.step-8 .p-state{color:#C3CAD3}
.p-item.step-8.st-live .p-state{color:#4ADE80}
.p-item.step-8 a:hover{background:#1F242C}

/* A step card. The global section padding is reset: the card's own header
   and body carry the spacing. */
.step{
  padding:0;margin:0 0 1.25rem;border:1px solid var(--sb);border-top:5px solid var(--sc);
  border-radius:calc(var(--r) + 4px);background:var(--paper, #ffffff);overflow:hidden;
  scroll-margin-top:5.5rem;
}
.step:target{box-shadow:0 0 0 4px var(--st),0 0 0 6px var(--sc)}
.step-head{
  display:grid;grid-template-columns:auto 1fr auto;gap:.35rem .9rem;align-items:start;
  padding:1rem 1.25rem;background:var(--st);border-bottom:1px solid var(--sb);
}
.step-num{
  width:2.4rem;height:2.4rem;border-radius:999px;background:var(--sc);color:#fff;display:grid;place-content:center;
  font-family:var(--display),system-ui,sans-serif;font-weight:800;font-size:1.15rem;align-self:center;
}
.step-lead{min-width:0}
.step-lead h2{font-size:1.2rem;margin:0 0 .2rem;color:var(--ink, #12161c)}
.step-lead p{margin:0;font-size:.92rem;color:var(--ink-2, #39424e);line-height:1.5;text-wrap:pretty}
.step-state{
  font-size:.7rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;
  border-radius:999px;padding:.3rem .65rem;background:var(--paper, #ffffff);color:var(--ink-2, #39424e);border:1px solid var(--sb);
  align-self:start;margin-top:.25rem;
}
.step-state.st-todo{background:var(--sc);color:#fff;border-color:var(--sc)}
.step-state.st-done,.step-state.st-live{background:var(--ok, #15803d);color:#fff;border-color:var(--ok, #15803d)}
.step-state.st-done::before,.step-state.st-live::before{content:"✓ "}
.step-state.st-locked{background:transparent;color:var(--muted, #6b7684);border-style:dashed}
.st-locked .step-head{opacity:.75}
.step-body{padding:1.25rem;display:grid;gap:.9rem}
.step-body-locked p{margin:0;color:var(--muted, #6b7684)}
.step-body-locked strong{color:var(--ink-2, #39424e)}
.step-sub-h{font-size:1rem;margin:.75rem 0 0;padding-top:.9rem;border-top:1px solid var(--rule, #e3e8ef)}
.step-sub-h .opt,.logo-actions h3 .opt{font-weight:400;color:var(--muted, #6b7684);font-family:var(--font),system-ui,sans-serif;font-size:.85rem}
.step .photo-strip{margin-bottom:.5rem}
.photo-strip>p{grid-column:1/-1;margin:0}
.step .form-inline .btn{justify-self:start}
.step .logo-row{margin-bottom:.25rem}
.req{
  display:inline-block;font-weight:700;font-size:.64rem;letter-spacing:.05em;text-transform:uppercase;
  color:var(--sc);background:var(--st);border:1px solid var(--sb);border-radius:999px;padding:.08rem .45rem;
  margin-left:.3rem;vertical-align:middle;
}
.svc-empty{padding:.9rem 1rem;border:1px dashed var(--sb);border-radius:var(--r);background:var(--st);color:var(--ink-2, #39424e)}
@media (min-width:48rem){
  .step-body{grid-template-columns:1fr 1fr}
  .step-body>*{grid-column:1/-1}
  .step-body>.field.half{grid-column:auto}
}
@media (max-width:30rem){
  .step-head{grid-template-columns:auto 1fr;padding:.9rem 1rem}
  .step-state{grid-column:1/-1;justify-self:start;margin-top:.1rem}
  .step-body{padding:1rem}
  .step-lead h2{font-size:1.08rem}
}

/* The save bar that closes Steps 1–5. Brand blue and unmissable, because
   "where do I save" was the question. */
.save-bar{
  display:flex;flex-wrap:wrap;gap:1rem;align-items:center;justify-content:space-between;
  padding:1.1rem 1.25rem;border:2px solid var(--brand, #1a73e8);border-radius:calc(var(--r) + 4px);
  background:var(--brand-tint, #e8f0fe);margin:0 0 1.75rem;scroll-margin-top:5.5rem;
}
.save-text{flex:1;min-width:14rem}
.save-text strong{display:block;font-family:var(--display),system-ui,sans-serif;font-size:1.05rem;color:var(--ink, #12161c)}
.save-text p{margin:.15rem 0 0;font-size:.9rem;color:var(--ink-2, #39424e)}
.btn-save{min-width:12rem;font-size:1rem}

/* The finish card. Dark on purpose: everything above it is a white card, and
   the one that makes the profile public should not look like one more. */
.step-finish{background:var(--ink, #12161c);color:#E5E9EF;border-color:var(--ink, #12161c)}
.step-finish .step-head{background:#1F242C;border-bottom-color:#2E3540}
.step-finish .step-lead h2{color:#fff}
.step-finish .step-lead p{color:#C3CAD3}
.step-finish .step-num{background:#fff;color:var(--ink, #12161c)}
.step-finish .step-state{background:transparent;color:#C3CAD3;border-color:#3A4250}
.step-finish .step-state.st-last{background:#fff;color:var(--ink, #12161c);border-color:#fff}
.step-finish .step-state.st-live{background:#22C55E;color:#0B2E16;border-color:#22C55E}
.finish-copy{margin:0;font-size:1rem;line-height:1.6;color:#E5E9EF;max-width:64ch}
.finish-copy strong{color:#fff}
.finish-live{margin:0;font-size:1.05rem;color:#fff}
.finish-live a,.btn-link-dark{color:#8FD3FF}
.finish-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:.25rem}
.finish-note{margin:.5rem 0 0;font-size:.9rem;color:#C3CAD3}
.finish-resend{margin-top:.25rem}
.btn-go{background:#22C55E;border-color:#22C55E;color:#0B2E16;font-weight:800;font-size:1.05rem;padding:.8rem 1.6rem}
.btn-go:hover{background:#16A34A;border-color:#16A34A;color:#fff}
.btn-go:disabled,.btn-go[aria-disabled=true]{opacity:.45;cursor:not-allowed}
.btn-go:disabled:hover{background:#22C55E;border-color:#22C55E;color:#0B2E16}
.btn-ghost-dark{background:transparent;border-color:#4B5563;color:#fff}
.btn-ghost-dark:hover{border-color:#fff;background:#1F242C}

/* Unsaved-changes bar, shown by the script once an existing profile is edited
   so the save button is never more than a thumb away. */
.unsaved{
  position:fixed;left:0;right:0;bottom:0;z-index:70;display:none;justify-content:center;
  padding:.6rem var(--pad) calc(.6rem + env(safe-area-inset-bottom, 0px));
  background:rgba(20,23,28,.94);color:#fff;backdrop-filter:blur(6px);box-shadow:0 -6px 24px rgb(20 23 28 / .25);
}
.unsaved.is-on{display:flex}
.unsaved-in{display:flex;gap:1rem;align-items:center;justify-content:space-between;width:100%;max-width:var(--wide)}
.unsaved strong{font-size:.95rem}
.unsaved small{display:block;color:#C3CAD3;font-size:.8rem}
body.has-unsaved{padding-bottom:5.5rem}

/* The three-line "what happens next" under the signup form. */
.join-next{
  list-style:none;margin:2rem 0 0;padding:1rem 1.1rem;display:grid;gap:.75rem;
  border:1px solid var(--rule, #e3e8ef);border-radius:calc(var(--r) + 2px);background:var(--sunk, #f5f7fa);max-width:32rem;
}
.join-next li{display:flex;gap:.7rem;align-items:flex-start;font-size:.92rem;color:var(--ink-2, #39424e);line-height:1.45}
.join-next .p-num{--sc:#1B6EF3;margin-top:.05rem}
.join-next li:nth-child(2) .p-num{--sc:#7C3AED}
.join-next li:nth-child(3) .p-num{--sc:#15803D}
.join-next strong{color:var(--ink, #12161c)}

/* The confirmation email comes from an address on a different domain and
   often lands in Spam. Both facts are stated wherever the email is mentioned,
   and the address is set in monospace so it reads as something to search for
   rather than as part of the sentence. */
.sender{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.92em;font-weight:700;overflow-wrap:anywhere;padding:.05rem .35rem;border-radius:4px;background:var(--paper, #ffffff);border:1px solid var(--rule-2, #cbd3df);color:var(--ink, #12161c)}
.sender-dark{background:#1F242C;border-color:#3A4250;color:#fff}
.sender-card{margin:.25rem 0 .9rem;padding:.8rem .95rem;border:1px solid #FDE68A;border-radius:var(--r);background:var(--paper, #ffffff)}
.sender-card p{margin:0 0 .45rem}
.sender-card p:last-child{margin-bottom:0}
.sender-line{font-size:1rem}
