/* ============================================================
   DESIGN TOKENS — BLUE IS THE PRIMARY VOICE
   Ramp sampled from the logo's gradient plus the blues already live on
   hookditup.com (#2b94fb, #146ff8) over its deep navy (#021343).

   Black is demoted to an accent: every dark surface is now NAVY
   (--color-dark), and text ink is #0d1b2a instead of #020202 — 17:1 on
   white, so identical legibility with none of the glare.

   Contrast rules that constrain the palette:
     #2da1db  2.9:1 on white -> accent only; never text or a button fill on light
     #2b94fb  3.1:1 on white -> glows and gradient stops only, never text
     #2466ac  5.9:1 on white -> carries every button and link
   ============================================================ */
:root{
  --blue-050:#f2f9fe;
  --blue-100:#e2f1fc;
  --blue-200:#c2e4f8;
  --blue-300:#7cc9f2;
  --blue-400:#2da1db;   /* logo bright */
  --blue-450:#2b94fb;   /* current-site vivid — glows/gradient stops only */
  --blue-500:#2466ac;   /* action blue */
  --blue-600:#1b4d84;
  --blue-700:#14396a;
  --blue-800:#0d2a50;
  --blue-900:#061b3d;   /* deep navy — replaces black on every dark surface */

  --color-primary:#2466ac;
  --color-primary-dark:#1b4d84;
  --color-primary-darker:#14396a;
  --color-accent:#2da1db;
  --color-accent-soft:#e2f1fc;
  --color-dark:#061b3d;       /* dark BACKGROUNDS use this, never --color-ink */
  --color-dark-2:#0a2547;     /* raised panels sitting on a dark surface */
  --color-ink:#0d1b2a;        /* text ink — softened from #020202 */
  --color-ink-soft:#16283a;
  --color-bg:#ffffff;
  --color-surface:#f2f9fe;
  --color-surface-2:#e2f1fc;
  --color-text:#16283a;
  --color-text-muted:#4f6377;
  --color-border:#d5e6f3;
  --color-border-strong:#9aa9b8;
  --color-steel:#9aa5ad;      /* the silver of the cable connectors in the logo */
  /* #f0a500 gold measures only 2.1:1 on white — fails even the 3:1 non-text
     minimum. #985c00 is 5.0:1 on white and still reads as gold/amber. */
  --color-star:#985c00;
  --color-star-on-dark:#f5b53d;

  --font-sans:"Plus Jakarta Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --font-display:"Orbitron","Plus Jakarta Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  --radius-sm:8px;
  --radius:12px;
  --radius-lg:18px;
  /* Shadows tinted navy instead of neutral black — reads softer against blue */
  --shadow-sm:0 1px 2px rgb(13 42 80 / .06);
  --shadow-md:0 4px 14px rgb(13 42 80 / .10);
  --shadow-lg:0 18px 44px rgb(13 42 80 / .18);
  --container:1200px;
  --section-y:clamp(3.25rem,8vw,6rem);
  --header-h:74px;
}

*,*::before,*::after{box-sizing:border-box}
/* An author `display` declaration beats the UA rule for [hidden], which would
   otherwise leave hidden elements visible (this shipped once as a duplicate
   logo + wordmark in the header). */
[hidden]{display:none !important}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;
  font-family:var(--font-sans);
  font-size:clamp(1rem,.97rem + .15vw,1.075rem);
  line-height:1.65;
  color:var(--color-text);
  background:var(--color-bg);
  -webkit-font-smoothing:antialiased;
}
img,svg{display:block;max-width:100%}
img{height:auto}
/* Inline SVGs carry only a viewBox, so they have NO intrinsic size — inside a flex
   container they stretch to 100% of the available width (this shipped once as
   244x244px arrows inside 52px buttons). This is the safety net. Specificity is
   0,0,1, so every `.class svg` rule below (0,1,1) still wins. */
svg{width:1.25em;height:1.25em;flex:none}
h1,h2,h3,h4{line-height:1.15;letter-spacing:-.02em;margin:0 0 .6em;color:var(--color-ink);font-weight:800}
h1{font-size:clamp(2.1rem,1.35rem + 3.4vw,3.75rem)}
h2{font-size:clamp(1.7rem,1.25rem + 2vw,2.6rem)}
h3{font-size:clamp(1.15rem,1.05rem + .5vw,1.4rem);font-weight:700}
p{margin:0 0 1.1em}
a{color:var(--color-primary);text-underline-offset:3px}
a:hover{color:var(--color-primary-dark)}
ul{margin:0;padding:0}
/* `strong` is semantic, not a colour. Hardcoding ink here painted dark text on
   every dark surface it landed on (measured 1.02:1 in the contact list, 1.21:1
   on the SUPPORT Utah card). It inherits now; the light-background contexts
   that genuinely want a darker bold set it themselves below. */
strong{font-weight:700}

/* Focus */
:focus-visible{outline:3px solid var(--color-accent);outline-offset:3px;border-radius:4px}
.on-dark :focus-visible{outline-color:#8fd3f4}

/* Layout */
.container{width:100%;max-width:var(--container);margin-inline:auto;padding-inline:clamp(1.1rem,4vw,2rem)}
.section{padding-block:var(--section-y)}
.section--surface{background:var(--color-surface)}
.section--dark{background:var(--color-dark);color:#c8dcea}
.section--dark h2,.section--dark h3{color:#fff}
section[id]{scroll-margin-top:calc(var(--header-h) + 12px)}
.eyebrow{
  display:inline-flex;align-items:center;gap:.5rem;
  font-family:var(--font-display);
  font-size:.76rem;font-weight:800;letter-spacing:.13em;text-transform:uppercase;
  color:var(--color-primary);margin:0 0 .9rem;
}
.section--dark .eyebrow{color:var(--color-accent)}
.section-head{max-width:none}
.section-head--center{max-width:56ch;margin-inline:auto;text-align:center}
.lede{font-size:clamp(1.05rem,1rem + .35vw,1.2rem);color:var(--color-text-muted);margin-bottom:0}
.section--dark .lede{color:#b7c4d3}

/* Skip link */
.skip-link{
  position:absolute;left:1rem;top:-100px;z-index:200;
  background:var(--color-dark);color:#fff;padding:.85rem 1.25rem;
  border-radius:var(--radius-sm);font-weight:700;text-decoration:none;
}
.skip-link:focus{top:1rem;color:#fff}

/* ---------------- Buttons ---------------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.55rem;
  min-height:52px;padding:.85rem 1.5rem;
  font:inherit;font-weight:700;font-size:1rem;line-height:1.2;
  border-radius:var(--radius);border:2px solid transparent;
  text-decoration:none;cursor:pointer;
  transition:background-color .18s ease,color .18s ease,border-color .18s ease,transform .18s ease,box-shadow .18s ease;
}
.btn:hover{transform:translateY(-2px)}
.btn svg{width:1.1em;height:1.1em;flex:none}
.btn--primary{background:var(--color-primary);color:#fff;box-shadow:var(--shadow-md)}
.btn--primary:hover{background:var(--color-primary-dark);color:#fff;box-shadow:var(--shadow-lg)}
.btn--ghost{background:transparent;color:var(--color-primary);border-color:var(--color-border-strong)}
.btn--ghost:hover{background:var(--color-accent-soft);border-color:var(--color-primary);color:var(--color-primary-dark)}
.btn--on-dark{background:#fff;color:var(--color-primary-darker)}
.btn--on-dark:hover{background:var(--color-accent-soft);color:var(--color-primary-darker)}
.btn--outline-dark{background:transparent;color:#fff;border-color:rgb(255 255 255 / .45)}
.btn--outline-dark:hover{background:rgb(255 255 255 / .1);color:#fff;border-color:#fff}
.btn--block{width:100%}
.btn-row{display:flex;flex-wrap:wrap;gap:.85rem}

/* ---------------- Header ---------------- */
.site-header{
  position:sticky;top:0;z-index:100;
  background:rgb(255 255 255 / .92);
  backdrop-filter:blur(10px);
  border-bottom:1px solid var(--color-border);
}
.header-inner{display:flex;align-items:center;gap:1rem;min-height:var(--header-h)}
.brand{display:inline-flex;align-items:center;gap:.6rem;min-height:44px;text-decoration:none;color:var(--color-ink);font-weight:800;font-size:1.075rem;letter-spacing:-.03em;flex:none}
.brand:hover{color:var(--color-primary-dark)}
/* ---- BRAND LOCKUP ----
   TO USE YOUR REAL BLUE LOGO: drop the file next to index.html and replace the
   <span class="wordmark"> in the header markup with
     <img class="brand-logo" src="logo-blue.svg" alt="Hook'd IT Up" width="300" height="80">
   The only logo file published on hookditup.com is the BLACK variant
   (hookditup-small.png), which fights the blue-forward direction, so the
   header uses a blue logotype until the real blue file is dropped in. */
.brand-logo{width:auto;height:clamp(30px,6vw,38px)}
.brand--footer{flex-direction:column;align-items:flex-start;gap:.35rem}
.brand-logo--white{height:clamp(34px,7vw,42px)}
/* Was 12.5px uppercase with letter-spacing, crammed beside a 44px-tall link —
   legible on paper (10.4:1) but genuinely hard to read. Own line, normal case,
   14px. */
.nap-hint{display:block;font-style:normal;font-size:.88rem;font-weight:600;
  color:#a8c0d6;text-transform:none;letter-spacing:0;margin:-.3rem 0 0;line-height:1.4}
/* Locality stacked UNDER the logo, not beside it */
.header-inner .brand{flex-direction:column;align-items:flex-start;gap:.1rem}
.header-inner .brand-sub{margin-top:0;padding-left:2px}
/* Scoped to .header-inner (0,2,0) so it beats the later `.wordmark{color:#fff}`
   rule (0,1,0). Unscoped, source order made this white-on-white. */
.header-inner .wordmark--light{color:var(--color-primary)}
.header-inner .wordmark--light i{color:var(--color-primary-darker)}
.brand-mark{width:34px;height:34px;border-radius:9px;background:var(--color-dark);color:var(--color-accent);display:grid;place-items:center;flex:none}
.brand-mark svg{width:20px;height:20px}
.brand-sub{display:block;font-family:var(--font-display);font-size:.6rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--color-text-muted);margin-top:1px}
/* Text logotype used on dark surfaces, where the dark-on-light logo file would
   disappear. Echoes the wordmark: heavy squared type with IT in brand blue. */
.wordmark{font-family:var(--font-display);font-size:clamp(1rem,.92rem + .35vw,1.2rem);font-weight:800;letter-spacing:-.015em;color:#fff;line-height:1.1;display:block;white-space:nowrap}
.wordmark i{font-style:normal;color:var(--color-accent)}

.nav-toggle{
  margin-left:auto;display:inline-flex;align-items:center;justify-content:center;
  width:48px;height:48px;background:transparent;border:1px solid var(--color-border);
  border-radius:var(--radius-sm);color:var(--color-ink);cursor:pointer;
}
.nav-toggle svg{width:24px;height:24px}
.nav-toggle .icon-close{display:none}
.nav-toggle[aria-expanded="true"] .icon-open{display:none}
.nav-toggle[aria-expanded="true"] .icon-close{display:block}

.site-nav{display:none}
.site-nav.is-open{display:block}
.site-nav ul{list-style:none;display:flex;flex-direction:column;gap:.15rem}
.site-nav ul a{
  display:block;padding:.8rem .25rem;font-weight:600;color:var(--color-text);
  text-decoration:none;border-bottom:1px solid var(--color-border);
}
.site-nav ul a:hover{color:var(--color-primary-dark)}
.header-cta{display:none}
.header-lead{display:none}
.header-phone{display:none}

@media (max-width:1023px){
  .site-nav{position:absolute;left:0;right:0;top:100%;background:#fff;border-bottom:1px solid var(--color-border);box-shadow:var(--shadow-md);padding:.5rem clamp(1.1rem,4vw,2rem) 1.35rem}
  .site-nav .nav-cta{margin-top:1rem;display:grid;gap:.6rem}
  /* Every child here already carries .btn, which supplies its own complete
     border and padding — a leftover reset here was overriding it and collapsing
     the phone/consultation buttons to unstyled, top-aligned text. */
}
@media (min-width:1024px){
  .nav-toggle{display:none}
  .site-nav{display:block;margin-left:auto}
  .site-nav ul{flex-direction:row;align-items:center;gap:.35rem}
  .site-nav ul a{border:0;padding:.6rem .7rem;min-height:44px;display:flex;align-items:center;border-radius:var(--radius-sm);font-size:.96rem}
  .site-nav ul a:hover{background:var(--color-surface)}
  .site-nav .nav-cta{display:none}
  .header-phone{display:inline-flex;align-items:center;gap:.45rem;font-weight:700;color:var(--color-ink);text-decoration:none;padding:.5rem .6rem;border-radius:var(--radius-sm);white-space:nowrap}
  .header-phone svg{width:18px;height:18px;color:var(--color-primary)}
  .header-phone:hover{background:var(--color-surface);color:var(--color-primary-dark)}
  .header-cta{display:inline-flex;min-height:46px;padding:.6rem 1.1rem;font-size:.95rem;flex:none}
  /* Lead magnet. Ghost styling so it reads as secondary to "Free consultation".
     The container caps at 1200px, so the header row has ~107px of slack at ANY
     desktop width — this only fits because FAQ moved to the footer and the nav
     links tightened below. */
  .header-lead{display:inline-flex;min-height:46px;padding:.6rem .8rem;font-size:.9rem;gap:.4rem;flex:none;white-space:nowrap}
  .header-lead svg{width:17px;height:17px;flex:none}
  .site-nav ul a{padding:.6rem .55rem;font-size:.92rem}

  /* ---- Services dropdown, desktop ---- */
  .has-menu{position:relative}
  /* `.site-nav ul` (0,1,1) also matches this list and would lay the items out
     in a row — needs a selector of at least equal weight to stack them. */
  .site-nav .nav-menu{display:block;flex-direction:column;width:auto}
  .nav-menu{
    position:absolute;top:calc(100% + 6px);left:0;z-index:120;
    min-width:268px;margin:0;padding:.4rem;list-style:none;
    background:#fff;border:1px solid var(--color-border);border-radius:var(--radius);
    box-shadow:0 18px 40px rgb(4 18 40 / .16),0 2px 6px rgb(4 18 40 / .08);
    opacity:0;visibility:hidden;transform:translateY(-6px);
    transition:opacity .16s ease,transform .16s ease,visibility .16s;
  }
  .has-menu.is-open .nav-menu{opacity:1;visibility:visible;transform:none}
  .site-nav .nav-menu a{
    display:block;width:100%;padding:.55rem .7rem;border-radius:var(--radius-sm);
    font-size:.92rem;font-weight:600;color:var(--color-ink);white-space:nowrap;min-height:0;
  }
  .site-nav .nav-menu a:hover{background:var(--color-accent-soft);color:var(--color-primary-dark)}
  .nav-menu-rule{height:1px;margin:.35rem .55rem;background:var(--color-border)}
  /* The free audit is the lead magnet — worth marking out from the paid services. */
  .site-nav .nav-menu a.nav-menu-free{color:var(--color-primary-dark);font-weight:700}
  .site-nav .nav-menu a.nav-menu-free::before{content:"";display:inline-block;width:7px;height:7px;
    margin-right:.5rem;border-radius:50%;background:var(--color-accent);vertical-align:middle}
  /* Bridges the 6px gap so the menu does not vanish as the pointer travels to it. */
  .has-menu::after{content:"";position:absolute;left:0;right:0;top:100%;height:8px}
}

/* ---- Dropdown trigger: styled to sit level with the sibling nav links ---- */
.nav-parent{
  display:flex;align-items:center;gap:.3rem;width:100%;
  background:none;border:0;font:inherit;font-weight:600;color:inherit;
  cursor:pointer;text-align:left;
}
.nav-caret{width:15px;height:15px;flex:none;transition:transform .18s ease}
.has-menu.is-open .nav-caret{transform:rotate(180deg)}

/* ---- Mobile drawer: the submenu expands inline, no hover involved ---- */
@media (max-width:1023px){
  .nav-parent{
    justify-content:space-between;padding:.85rem .25rem;min-height:48px;
    border-bottom:1px solid var(--color-border);
  }
  .site-nav .nav-menu{display:block;width:auto}
  .nav-menu{
    list-style:none;margin:0;padding:0;
    max-height:0;overflow:hidden;transition:max-height .22s ease;
  }
  .has-menu.is-open .nav-menu{max-height:34rem}
  .site-nav .nav-menu a{padding-left:1.1rem;font-size:.94rem;color:var(--color-text-muted)}
  .nav-menu-rule{display:none}
}

/* Between the mobile breakpoint (1024px) and a comfortable desktop, the header row
   (brand + 7 nav links + phone + CTA) needs ~1092px but only has ~960px, so the CTA
   pushed past the viewport and created a horizontal scrollbar. Tighten the band
   rather than dropping anything the visitor needs. -- header squeeze band */
@media (min-width:1024px) and (max-width:1299px){
  .header-inner{gap:.6rem}
  .site-header .container{padding-inline:1rem}
  .site-nav ul{gap:.1rem}
  .site-nav ul a{padding:.6rem .45rem;font-size:.88rem}
  .header-phone{padding:.5rem .35rem;font-size:.9rem;gap:.35rem}
  .header-phone svg{width:16px;height:16px}
  .header-cta{padding:.6rem .8rem;font-size:.88rem}
  .header-lead{padding:.6rem .6rem;font-size:.84rem;gap:.3rem}
  .header-lead svg{width:15px;height:15px}
  .brand-logo{height:30px}
  .brand-sub{font-size:.54rem;letter-spacing:.1em}
}

/* ---------------- Hero ---------------- */
.hero{
  position:relative;overflow:hidden;
  /* Reads unmistakably BLUE, not black: vivid glows over a navy-to-blue base. */
  background:
    radial-gradient(920px 540px at 86% -10%,rgb(43 148 251 / .55),transparent 60%),
    radial-gradient(760px 500px at 4% 108%,rgb(45 161 219 / .38),transparent 60%),
    linear-gradient(158deg,#0d2a50 0%,#061b3d 52%,#0a2547 100%);
  color:#c8dcea;
  padding-block:clamp(2.75rem,7vw,4.75rem);
}
/* Skewed accent slab — the diagonal blue geometry from hookditup.com */
.hero::before{
  content:"";position:absolute;z-index:0;pointer-events:none;
  right:-10%;top:-25%;width:54%;height:160%;
  transform:skewX(-11deg);
  background:linear-gradient(180deg,rgb(43 148 251 / .20),rgb(45 161 219 / .04));
  border-left:2px solid rgb(45 161 219 / .40);
}
.hero::after{
  content:"";position:absolute;inset:0;pointer-events:none;opacity:.4;
  background-image:linear-gradient(rgb(255 255 255 / .05) 1px,transparent 1px),linear-gradient(90deg,rgb(255 255 255 / .05) 1px,transparent 1px);
  background-size:52px 52px;
  mask-image:radial-gradient(60% 60% at 50% 40%,#000,transparent);
}
.hero .container{position:relative;z-index:1}
.hero-grid{display:grid;gap:clamp(2.25rem,5vw,3.5rem);align-items:center}
.hero h1{color:#fff;text-wrap:balance}
.hero h1 .hl{color:var(--color-accent)}
.hero-lede{font-size:clamp(1.075rem,1rem + .5vw,1.3rem);color:#c3d0de;max-width:46ch}
.hero-badge{
  display:inline-flex;align-items:center;gap:.5rem;
  font-family:var(--font-display);
  background:rgb(45 161 219 / .16);border:1px solid rgb(45 161 219 / .42);
  color:#a9dcf6;font-size:.82rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  padding:.45rem .85rem;border-radius:999px;margin-bottom:1.35rem;
}
.hero-badge svg{width:15px;height:15px}
.hero-proof{list-style:none;display:flex;flex-wrap:wrap;gap:.5rem 1.4rem;margin-top:1.75rem;padding-top:1.5rem;border-top:1px solid rgb(255 255 255 / .14)}
.hero-proof li{display:flex;align-items:center;gap:.5rem;font-size:.94rem;color:#bdcbda;font-weight:500}
.hero-proof svg{width:18px;height:18px;color:var(--color-accent);flex:none}
.hero-note{font-size:.88rem;color:#93a4b6;margin:1rem 0 0}

/* Hero visual: pure CSS map-pack mock, zero image dependencies */
.mock{
  background:#fff;border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);
  color:var(--color-text);overflow:hidden;border:1px solid rgb(255 255 255 / .5);
}
.mock-bar{display:flex;align-items:center;gap:.6rem;padding:.8rem 1rem;background:var(--color-surface);border-bottom:1px solid var(--color-border)}
.mock-search{flex:1;background:#fff;border:1px solid var(--color-border);border-radius:999px;padding:.5rem .9rem;font-size:.86rem;color:var(--color-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mock-search b{color:var(--color-ink);font-weight:700}
.mock-body{padding:1rem 1.1rem 1.2rem}
.mock-label{font-family:var(--font-display);font-size:.68rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:.7rem}
.mock-list{list-style:none;display:grid;gap:.55rem}
.mock-item{display:flex;align-items:center;gap:.75rem;padding:.7rem .8rem;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:#fff}
.mock-item--win{border-color:var(--color-accent);background:var(--color-accent-soft);box-shadow:0 0 0 3px rgb(45 161 219 / .16)}
.mock-pin{width:30px;height:30px;border-radius:8px;background:var(--color-surface-2);color:var(--color-text-muted);display:grid;place-items:center;font-weight:800;font-size:.82rem;flex:none}
.mock-item--win .mock-pin{background:var(--color-primary);color:#fff}
.mock-name{font-weight:700;font-size:.94rem;line-height:1.3}
.mock-meta{font-size:.79rem;color:var(--color-text-muted);display:flex;align-items:center;gap:.3rem}
.mock-meta .stars{color:var(--color-star);letter-spacing:-.5px}
.mock-tag{margin-left:auto;font-size:.68rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--color-primary-darker);background:#fff;border:1px solid var(--color-accent);border-radius:999px;padding:.2rem .5rem;flex:none}
.mock-foot{display:flex;align-items:center;gap:.6rem;margin-top:1rem;padding-top:.9rem;border-top:1px dashed var(--color-border);font-size:.84rem;color:var(--color-text-muted)}
.mock-foot svg{width:18px;height:18px;color:var(--color-primary);flex:none}

@media (min-width:900px){
  .hero-grid{grid-template-columns:1.08fr .92fr}
}

/* ---------------- Stat bar ---------------- */
.statbar{background:var(--color-surface);border-bottom:1px solid var(--color-border)}
.statbar-grid{list-style:none;display:grid;grid-template-columns:repeat(2,1fr);gap:1.35rem 1rem;padding-block:clamp(1.85rem,4vw,2.5rem)}
.stat{text-align:center}
.stat dt{font-size:.8rem;font-weight:600;color:var(--color-text-muted);line-height:1.35}
.stat dd{margin:0 0 .15rem;font-size:clamp(1.6rem,1.2rem + 1.6vw,2.35rem);font-weight:800;letter-spacing:-.035em;color:var(--color-primary-darker)}
.stat .stars{color:var(--color-star);font-size:1.5rem;letter-spacing:-1px}
@media (min-width:768px){.statbar-grid{grid-template-columns:repeat(4,1fr)}}

/* ---------------- Cards / grids ---------------- */
.grid{display:grid;gap:1.15rem}
.grid--3{grid-template-columns:1fr}
.grid--2{grid-template-columns:1fr}
.grid--4{grid-template-columns:1fr}
@media (min-width:640px){
  .grid--4{grid-template-columns:repeat(2,1fr)}
  .grid--3{grid-template-columns:repeat(2,1fr)}
}
@media (min-width:768px){.grid--2{grid-template-columns:repeat(2,1fr)}}
/* The four premier-service cards carry tick lists, so a 4-up row only has ~266px
   per card at container width. Hold 2x2 until there is genuinely room for four,
   and tighten the type a notch once they go side by side. */
.grid--svc{grid-template-columns:1fr}
@media (min-width:640px){.grid--svc{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:1120px){
  .grid--svc{grid-template-columns:repeat(4,minmax(0,1fr));gap:1.1rem}
  .grid--svc .card{padding:1.5rem 1.35rem}
  .grid--svc .card h3{font-size:1.08rem}
  .grid--svc .card>p{font-size:.92rem}
  .grid--svc .ticks li{font-size:.88rem;line-height:1.45}
}
@media (min-width:1024px){
  .grid--3{grid-template-columns:repeat(3,1fr)}
  .grid--4{grid-template-columns:repeat(4,1fr)}
}

.card{
  background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-lg);
  padding:clamp(1.4rem,3vw,1.9rem);box-shadow:var(--shadow-sm);
  display:flex;flex-direction:column;
  transition:box-shadow .2s ease,transform .2s ease,border-color .2s ease;
}
.card:hover{box-shadow:var(--shadow-md);transform:translateY(-3px);border-color:var(--color-accent)}
.card h3{margin-bottom:.5rem}
.card p{color:var(--color-text-muted)}
.card p:last-of-type{margin-bottom:1.15rem}
.icon-tile{
  width:52px;height:52px;border-radius:var(--radius);flex:none;
  background:var(--color-accent-soft);color:var(--color-primary-darker);
  display:grid;place-items:center;margin-bottom:1.1rem;border:1px solid #cfe8f7;
}
.icon-tile svg{width:26px;height:26px}
.ticks{list-style:none;display:grid;gap:.6rem;margin:0 0 1.35rem}
.ticks li{position:relative;padding-left:1.85rem;font-size:.96rem;color:var(--color-text-muted)}
.ticks li::before{
  content:"";position:absolute;left:0;top:.34em;width:18px;height:18px;
  background:var(--color-primary);border-radius:50%;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center/12px no-repeat;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center/12px no-repeat;
}
.card-link{margin-top:auto;font-weight:700;text-decoration:none;display:inline-flex;align-items:center;gap:.4rem;min-height:44px;align-self:flex-start}
/* On a dark section the inherited link blue (#2466ac) sits at roughly 2.3:1 on
   the navy — legible only if you already know what it says. White instead. */
.on-dark .card-link{color:#fff}
.card-link svg{width:17px;height:17px;transition:transform .18s ease}
.card-link:hover{text-decoration:underline}
.card-link:hover svg{transform:translateX(3px)}

/* Secondary services */
.also{margin-top:1.15rem;display:grid;gap:1.15rem;grid-template-columns:1fr}
@media (min-width:600px){.also{grid-template-columns:repeat(2,1fr)}}
@media (min-width:1024px){.also{grid-template-columns:repeat(4,1fr)}}
.mini{
  background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius);
  padding:1.15rem 1.25rem;
}
.mini h3{font-size:1.02rem;margin-bottom:.3rem}
.mini p{font-size:.9rem;color:var(--color-text-muted);margin:0}
.mini a{font-size:.9rem;font-weight:700;text-decoration:none;display:inline-flex;align-items:center;gap:.35rem;margin-top:.6rem;min-height:44px}
.mini a svg{width:1em;height:1em;flex:none}
.mini a:hover{text-decoration:underline}

/* ---------------- AI spotlight ---------------- */
.ai-grid{display:grid;gap:clamp(2rem,5vw,3.25rem);align-items:center}
@media (min-width:900px){.ai-grid{grid-template-columns:1fr 1fr}}
.chat{
  background:var(--color-dark-2);border:1px solid rgb(255 255 255 / .14);border-radius:var(--radius-lg);
  padding:1.15rem;box-shadow:var(--shadow-lg);
}
.chat-head{display:flex;align-items:center;gap:.65rem;padding-bottom:.9rem;margin-bottom:.9rem;border-bottom:1px solid rgb(255 255 255 / .12)}
.chat-avatar{width:36px;height:36px;border-radius:50%;background:var(--color-accent);color:#04121c;display:grid;place-items:center;flex:none}
.chat-avatar svg{width:20px;height:20px}
.chat-who{font-weight:700;color:#fff;font-size:.94rem;line-height:1.2}
.chat-status{font-size:.76rem;color:#8fd3f4;display:flex;align-items:center;gap:.35rem}
.dot{width:7px;height:7px;border-radius:50%;background:#3ddc84;flex:none}
.bubbles{list-style:none;display:grid;gap:.6rem}
.bubble{max-width:88%;padding:.7rem .95rem;border-radius:14px;font-size:.93rem;line-height:1.5}
.bubble--them{background:rgb(255 255 255 / .1);color:#e3ecf5;border-bottom-left-radius:5px;justify-self:start}
.bubble--ai{background:var(--color-primary);color:#fff;border-bottom-right-radius:5px;justify-self:end}
.chat-foot{margin:.95rem 0 0;font-size:.8rem;color:#93a4b6;display:flex;align-items:center;gap:.45rem}
.chat-foot svg{width:16px;height:16px;color:var(--color-accent);flex:none}
.ai-points{list-style:none;display:grid;gap:1rem;margin:0 0 1.75rem}
.ai-points li{display:flex;gap:.85rem;align-items:flex-start}
.ai-points .icon-tile{width:42px;height:42px;margin:0;background:rgb(45 161 219 / .16);border-color:rgb(45 161 219 / .38);color:#8fd3f4}
.ai-points .icon-tile svg{width:21px;height:21px}
.ai-points b{display:block;color:#fff;font-size:1rem}
.ai-points span{font-size:.94rem;color:#b7c4d3}

/* ---------------- About ---------------- */
.about-grid{display:grid;gap:clamp(2rem,5vw,3.25rem);align-items:start}
.about-grid strong{color:var(--color-ink)}
@media (min-width:900px){.about-grid{grid-template-columns:.85fr 1.15fr}}
/* Owner card — CSS only, needs no photo.
   TO USE A REAL PHOTO INSTEAD: replace the .owner-top block in the markup with
   <img src="josh.jpg" alt="Josh Hook, owner of Hook'd IT Up" width="800" height="1000">
   and add .owner-card img{width:100%;aspect-ratio:4/5;object-fit:cover} */
.owner-card{
  border-radius:var(--radius-lg);overflow:hidden;background:#fff;
  box-shadow:var(--shadow-md);border:1px solid var(--color-border);
}
/* The source photo is a TRANSPARENT CUTOUT (Josh + a laptop/phone mockup), so
   whatever sits behind it shows through. The old blue gradient was bleeding
   around him — that was the "weird blue". This is now a clean light panel, and
   background-size:contain means nothing gets cropped (head included).
   SELF-HOST A COPY before launch — see the asset checklist. */
.owner-top{
  position:relative;overflow:hidden;
  background:var(--color-surface-2);
  border-bottom:1px solid var(--color-border);
}
.owner-photo{
  display:block;width:100%;aspect-ratio:63/64;
  background-image:url("josh-hook.webp");
  background-size:contain;background-position:center bottom;background-repeat:no-repeat;
}
/* Fallback monogram, only visible if the photo fails to load */
.owner-mark{position:absolute;z-index:0;top:50%;left:50%;transform:translate(-50%,-50%)}
.owner-photo{position:relative;z-index:1}
/* Name block sits BELOW the photo on white, so no dark scrim is needed */
/* The name band used to be its own row under the photo, which made the card 90px
   taller than the column beside it. Overlaid on the bottom of the photo instead:
   same information, card now pairs with its neighbour. */
.owner-id{
  position:absolute;left:0;right:0;bottom:0;z-index:2;
  padding:3rem 1.15rem 1rem;border-bottom:0;
  background:linear-gradient(to top,rgb(4 18 40 / .94) 0%,rgb(4 18 40 / .82) 42%,rgb(4 18 40 / 0) 100%);
}
.owner-id .owner-name{color:#fff;font-size:1.24rem;text-shadow:0 1px 12px rgb(0 0 0 / .5)}
.owner-id .owner-role{color:#c3e4f8;font-size:.88rem;text-shadow:0 1px 10px rgb(0 0 0 / .55)}
/* Fallback only — sits behind .owner-photo and shows through if the photo fails */
.owner-mark{
  position:absolute;z-index:1;top:26%;left:50%;transform:translateX(-50%);
  width:clamp(84px,17vw,102px);height:clamp(84px,17vw,102px);margin:0;
  border-radius:26px;display:grid;place-items:center;
  background:var(--color-accent);color:#03141f;
  font-size:clamp(1.95rem,1.4rem + 1.9vw,2.5rem);font-weight:800;letter-spacing:-.055em;
  box-shadow:0 12px 32px rgb(45 161 219 / .34);
}
.owner-name{color:#fff;font-size:1.32rem;font-weight:800;letter-spacing:-.03em;margin:0}
.owner-role{color:#a9dcf6;font-size:.9rem;font-weight:600;margin:.25rem 0 0}
.owner-facts{list-style:none;display:grid}
.owner-facts li{
  display:flex;align-items:center;gap:.75rem;padding:.85rem 1.15rem;
  font-size:.9rem;color:var(--color-text-muted);border-top:1px solid var(--color-border);
}
.owner-facts svg{width:18px;height:18px;color:var(--color-primary);flex:none}
.owner-facts b{color:var(--color-ink);font-weight:700}
.pledges{list-style:none;display:grid;gap:.9rem;margin:1.5rem 0 0;padding:1.5rem 0 0;border-top:1px solid var(--color-border)}
.pledges li{display:flex;gap:.75rem;align-items:flex-start;font-size:.97rem;color:var(--color-text-muted)}
.pledges svg{width:22px;height:22px;color:var(--color-primary);flex:none;margin-top:.15rem}
.pledges b{color:var(--color-ink)}

/* ---------------- Process ---------------- */
/* Numbered step cards. The steps used to be wired together with a decorative
   ethernet run — a CSS patch cable with RJ45 plugs seated on each number badge.
   It has been removed at every breakpoint: it drew the eye along the row
   instead of into the cards, and it constrained the grid to exactly four
   columns. The numbers carry the sequence on their own. */
.steps{list-style:none;counter-reset:step;display:grid;gap:1.15rem;grid-template-columns:1fr;position:relative}
@media (min-width:640px){.steps{grid-template-columns:repeat(2,1fr)}}
@media (min-width:1024px){
  .steps{grid-template-columns:repeat(4,1fr)}
  /* Five-step variants keep all five on one row rather than orphaning the
     fifth onto a second. */
  .steps--5{grid-template-columns:repeat(5,1fr)}
  .steps--5 .step{padding:1.5rem 1.15rem 1.25rem}
}
.step{z-index:1}
.step{position:relative;background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:1.6rem 1.35rem 1.35rem}
.step::before{
  counter-increment:step;content:counter(step);
  position:absolute;top:-16px;left:1.35rem;
  width:38px;height:38px;border-radius:11px;background:var(--color-dark);color:var(--color-accent);
  display:grid;place-items:center;font-weight:800;font-size:1.05rem;
}
.step h3{margin:.85rem 0 .4rem;font-size:1.1rem}
.step p{margin:0;font-size:.95rem;color:var(--color-text-muted)}
.step-when{display:inline-block;margin-top:.8rem;font-family:var(--font-display);font-size:.72rem;font-weight:800;letter-spacing:.09em;text-transform:uppercase;color:var(--color-primary-darker);background:var(--color-accent-soft);border-radius:999px;padding:.25rem .6rem}

/* ---------------- Reviews ---------------- */
.rating-strip{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:.5rem .9rem;
  margin-bottom:2.25rem;font-weight:600;color:var(--color-text-muted);
}
.rating-strip .score{font-size:1.5rem;font-weight:800;color:var(--color-ink);letter-spacing:-.03em}
.rating-strip .stars{color:var(--color-star);font-size:1.35rem;letter-spacing:-1px}
blockquote.review{
  margin:0;background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-lg);
  padding:clamp(1.4rem,3vw,1.85rem);box-shadow:var(--shadow-sm);display:flex;flex-direction:column;
}
blockquote.review .stars{color:var(--color-star);font-size:1.1rem;letter-spacing:-1px;margin-bottom:.75rem}
blockquote.review p{font-size:1rem;color:var(--color-text);margin-bottom:1.15rem}
blockquote.review footer{margin-top:auto;display:flex;align-items:center;gap:.7rem;font-size:.92rem;color:var(--color-text-muted)}
.avatar-initial{
  width:38px;height:38px;border-radius:50%;background:var(--color-primary);color:#fff;
  display:grid;place-items:center;font-weight:800;flex:none;font-size:1rem;
}
blockquote.review cite{font-style:normal;font-weight:700;color:var(--color-ink);display:block}
blockquote.review .src{font-size:.82rem;color:var(--color-text-muted)}

/* The client ribbon is its own band now, so it carries no divider of its own */
.logos{margin-top:0;padding-top:0;border-top:0}
.section--ribbon{
  background:var(--color-bg);
  padding-block:clamp(2.25rem,5vw,3.25rem);
  border-bottom:1px solid var(--color-border);
}
/* --- Social proof + blog on navy --- */
.section--dark .rating-strip{color:#b7c4d3}
.section--dark .rating-strip .score{color:#fff}
.section--dark .logos-label{color:#8fd3f4}
/* White cards keep their own dark type on the navy background */
.section--dark blockquote.review,.section--dark .post-card{border-color:rgb(255 255 255 / .16)}
.section--dark blockquote.review p{color:var(--color-text)}
.section--dark .post-card h2 a{color:var(--color-ink)}
.section--dark .post-card p{color:var(--color-text-muted)}
.section--dark .post-card .pm{color:var(--color-text-muted)}
.logos-label{text-align:center;font-family:var(--font-display);font-size:.76rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:1.25rem}
.logo-row{list-style:none;display:grid;grid-template-columns:repeat(2,1fr);gap:1.25rem;align-items:center}
@media (min-width:640px){.logo-row{grid-template-columns:repeat(3,1fr)}}
@media (min-width:1024px){.logo-row{grid-template-columns:repeat(5,1fr)}}
/* ---------------- Client slider ----------------
   Auto-scrolling marquee. Each slide holds BOTH an <img> (logos/<slug>.png) and
   a text wordmark. The script removes the <img> if the file isn't there and
   marks the slide .no-logo, which reveals the text. So: drop the 20 files into
   a logos/ folder and every slide upgrades itself with no code change, and
   until then nothing renders as a broken image. */
.slider{
  position:relative;overflow:hidden;
  padding-block:.5rem;
  mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
}
.slider-track{
  display:flex;align-items:stretch;gap:1rem;width:max-content;
  animation:slide 62s linear infinite;
}
.slider:hover .slider-track,.slider:focus-within .slider-track{animation-play-state:paused}
@keyframes slide{from{transform:translateX(0)}to{transform:translateX(-50%)}}
.slider-track li{
  flex:0 0 auto;width:172px;min-height:88px;
  display:grid;place-items:center;text-align:center;
  padding:.8rem .75rem;
  border:1px solid var(--color-border);border-radius:var(--radius);
  background:#fff;box-shadow:var(--shadow-sm);
  color:var(--color-text-muted);
  font-size:.86rem;font-weight:800;letter-spacing:-.02em;line-height:1.25;
  transition:color .2s ease,border-color .2s ease,box-shadow .2s ease;
}
.slider-track li:hover{color:var(--color-ink);border-color:var(--color-accent);box-shadow:var(--shadow-md)}
.slider-track img{max-height:58px;width:auto;max-width:100%;object-fit:contain}
/* For marks that ship as light ink on a solid dark square: the tile matches the
   logo's own background so the square disappears and the mark floats, instead of
   sitting on white as a dark blob. */
.slider-track li.logo--plate{background:#010101;border-color:#010101}
.slider-track li.logo--plate:hover{border-color:var(--color-accent)}
.slider-track li.logo--plate img{max-height:66px}
/* Very wide banner marks (roughly 6:1 and wider) are width-limited long before
   they hit max-height, so they render tiny. A wider tile evens them out. */
.slider-track li.logo--wide{width:270px}
.slider-track li.logo--wide img{max-height:58px}
/* Mid-wide marks (~2.2:1 to 5.5:1). Cropping their dead margins made them wider,
   which meant the 148px tile width capped their height and they got SMALLER.
   A wider tile plus a taller cap restores comparable visual weight. */
.slider-track li.logo--mid{width:248px}
.slider-track li.logo--mid img{max-height:78px}
/* Square/portrait marks are limited by height, not width, so at 58px they used only
   ~30-50% of the tile while wide marks filled it. A taller budget evens them out. */
.slider-track li.logo--tall img{max-height:76px}
/* Portrait marks (taller than wide) still can't fill a landscape tile at 76px —
   give them a little more height so they read at a comparable visual size. */
.slider-track li.logo--portrait img{max-height:88px}
.slider-track li.logo--tall{min-height:100px}
.slider-track li{min-height:100px}
.slider-track .cname{display:none}
.slider-track li.no-logo .cname{display:block}
.slider-track .cname small{display:block;font-size:.74rem;font-weight:600;letter-spacing:0;margin-top:.15rem}
/* No animation for reduced-motion users — wrap into a static grid instead */
@media (prefers-reduced-motion:reduce){
  .slider{mask-image:none;-webkit-mask-image:none}
  .slider-track{animation:none;flex-wrap:wrap;width:100%;justify-content:center}
  .slider-track li[data-clone]{display:none}
  /* WCAG 2.3.3. The slider was covered but the scroll-reveals, hover lifts and
     smooth scrolling were not — those are the ones that trigger vestibular
     symptoms. Content must still be visible, so .reveal resets to its end state
     rather than simply losing its transition. */
  .reveal{opacity:1 !important;transform:none !important;transition:none !important}
  *,*::before,*::after{
    animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-duration:.001ms !important;scroll-behavior:auto !important;
  }
}

/* Windows High Contrast / forced-colors. Our buttons and cards are drawn with
   background colours, which the OS strips — without explicit borders they
   collapse into invisible rectangles. */
@media (forced-colors:active){
  .btn,.card,.price,.eco,.owner-card,.form-card,.modal-panel,.slider-track li{
    border:1px solid ButtonBorder;
  }
  .btn{forced-color-adjust:none;background:ButtonFace;color:ButtonText}
  :focus-visible{outline:3px solid Highlight;outline-offset:3px}
  .icon-tile,.socials a{border:1px solid ButtonBorder}
}

/* WCAG 2.5.8 - pointer targets at least 24x24 CSS px. The footer/legal links and
   social icons were the only ones under it. */
.footer-list a,.footer-bar a,.crumbs a,.toc a,.related a{
  display:inline-block;min-height:24px;padding-block:2px;
}
.socials a{min-width:40px;min-height:40px}

/* ---------------- Service area ---------------- */
.area-grid{display:grid;gap:clamp(1.75rem,4vw,3rem);align-items:center}
@media (min-width:900px){.area-grid{grid-template-columns:1fr 1fr}}
.towns{list-style:none;display:flex;flex-wrap:wrap;gap:.6rem;margin:0 0 1.5rem}
.towns li{
  background:#fff;border:1px solid var(--color-border);border-radius:999px;
  padding:.5rem .95rem;font-size:.92rem;font-weight:600;color:var(--color-text);
  display:flex;align-items:center;gap:.4rem;
}
.towns svg{width:15px;height:15px;color:var(--color-primary)}
/* Styled location panel used until a real Google embed is pasted in.
   .map-embed is kept below so swapping to the real iframe is a one-line change. */
.map-card{
  position:relative;overflow:hidden;border-radius:var(--radius-lg);
  border:1px solid var(--color-border);box-shadow:var(--shadow-md);
  background:radial-gradient(560px 360px at 80% 8%,rgb(43 148 251 / .38),transparent 62%),var(--color-dark);
  padding:clamp(1.6rem,4vw,2.25rem);
}
.map-card::after{
  content:"";position:absolute;inset:0;opacity:.3;pointer-events:none;
  background-image:linear-gradient(rgb(255 255 255 / .07) 1px,transparent 1px),linear-gradient(90deg,rgb(255 255 255 / .07) 1px,transparent 1px);
  background-size:42px 42px;
}
.map-card>*{position:relative;z-index:1}
.map-pin{
  width:46px;height:46px;border-radius:14px;display:grid;place-items:center;margin-bottom:1.1rem;
  background:rgb(45 161 219 / .18);border:1px solid rgb(45 161 219 / .42);color:#8fd3f4;
}
.map-pin svg{width:24px;height:24px}
.map-label{font-family:var(--font-display);font-size:.74rem;font-weight:800;letter-spacing:.13em;text-transform:uppercase;color:#8fd3f4;margin:0 0 .5rem}
.map-addr{color:#fff;font-size:clamp(1.2rem,1.05rem + .6vw,1.5rem);font-weight:800;letter-spacing:-.03em;line-height:1.35;margin:0 0 1.25rem}
.map-facts{list-style:none;display:grid;gap:.7rem;margin:0 0 1.5rem;padding-top:1.25rem;border-top:1px solid rgb(255 255 255 / .15)}
.map-facts li{display:flex;align-items:flex-start;gap:.65rem;font-size:.93rem;color:#c3d0de}
.map-facts svg{width:18px;height:18px;color:#8fd3f4;flex:none;margin-top:.18rem}
.map-embed{border-radius:var(--radius-lg);overflow:hidden;border:1px solid var(--color-border);box-shadow:var(--shadow-md);background:var(--color-surface-2)}
.map-embed iframe{display:block;width:100%;height:340px;border:0}

/* ---------------- Pricing ---------------- */
.price-group-h{
  display:flex;align-items:center;gap:.95rem;
  font-family:var(--font-display);
  font-size:.78rem;font-weight:800;letter-spacing:.13em;text-transform:uppercase;
  color:var(--color-text-muted);margin:clamp(2rem,4vw,2.75rem) 0 1.2rem;
}
.price-group-h::after{content:"";flex:1;height:1px;background:var(--color-border)}
.price{
  background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-lg);
  padding:clamp(1.4rem,3vw,1.85rem);box-shadow:var(--shadow-sm);
  display:flex;flex-direction:column;
  transition:box-shadow .2s ease,transform .2s ease,border-color .2s ease;
}
.price:hover{box-shadow:var(--shadow-md);transform:translateY(-3px);border-color:var(--color-accent)}
.price--feature{border-color:var(--color-accent);box-shadow:0 0 0 3px rgb(45 161 219 / .14),var(--shadow-md)}
.price-name{font-size:1.15rem;font-weight:800;margin:0 0 .7rem;color:var(--color-ink);letter-spacing:-.02em}
.price-tag{display:flex;align-items:baseline;flex-wrap:wrap;gap:.4rem;margin:0 0 .9rem}
.price-from{font-family:var(--font-display);font-size:.74rem;font-weight:800;letter-spacing:.09em;text-transform:uppercase;color:var(--color-text-muted)}
.price-amt{font-size:clamp(2rem,1.6rem + 1.4vw,2.5rem);font-weight:800;letter-spacing:-.045em;color:var(--color-primary-darker);line-height:1}
/* App development is quoted, not priced. Sized down from the dollar figures so the
   card still lines up on the same baseline instead of shouting a non-number. */
.price-amt--quote{font-size:clamp(1.45rem,1.2rem + .8vw,1.75rem);letter-spacing:-.02em}
.price-per{font-size:.95rem;font-weight:700;color:var(--color-text-muted)}
.price-desc{color:var(--color-text-muted);font-size:.95rem}
.price .ticks{margin-bottom:1.4rem}
.price .btn{margin-top:auto}

/* App Development spans the whole row. On its own it would be a very wide card
   with one narrow column of text, so once there is room the pitch sits left and
   the tick list goes two-up on the right. Below that it stays a normal card. */
.price--full{grid-column:1/-1}
/* 1000px, not 860 — below this the two tick columns squeeze to ~180px and every
   line wraps three deep, which reads worse than a plain stacked card. */
@media (min-width:1000px){
  .price--full{
    display:grid;
    grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);
    /* Named areas, not line numbers: the fine print was added later and would
       otherwise auto-place into whatever cell happened to be free. */
    /* Ticks and fine print share one wrapper so the fine print sits directly
       under the checkboxes. Placing them as separate grid areas left an 89px
       gap, because the row heights are driven by the taller left column. */
    grid-template-areas:
      "name col2"
      "tag  col2"
      "desc col2"
      "btn  col2";
    grid-template-rows:auto auto auto 1fr;
    column-gap:clamp(1.75rem,4vw,3.25rem);
    align-items:start;
  }
  .price--full>.price-name{grid-area:name;font-size:1.3rem}
  .price--full>.price-tag {grid-area:tag}
  .price--full>.price-desc{grid-area:desc}
  /* Centred, not top-aligned: the pitch column runs ~75px taller, which left the
     checkboxes hanging at the top with dead space under them. */
  .price--full>.price-col2{grid-area:col2;align-self:center}
  .price--full .ticks{
    grid-template-columns:repeat(2,minmax(0,1fr));
    column-gap:clamp(1.25rem,2.5vw,2rem);
    margin:.15rem 0 1.1rem;
  }
  .price--full .price-fineprint{margin:0}
  .price--full>.btn{grid-area:btn;align-self:end;margin:1.5rem 0 0}
  .price--full .price-amt--quote{font-size:1.95rem}
}

/* Asterisk tying the two cards with ongoing fees to the note under the section. */
.price-ast{
  display:inline-block;margin-left:.12rem;vertical-align:super;
  font-size:.7em;font-weight:800;line-height:1;
  color:var(--color-primary);text-decoration:none;
}
.price-ast:hover,.price-ast:focus-visible{color:var(--color-accent);text-decoration:underline}
.guarantee{
  display:flex;gap:.75rem;align-items:flex-start;
  background:var(--color-accent-soft);border:1px solid #cfe8f7;border-radius:var(--radius);
  padding:.9rem 1rem;margin:0 0 1.25rem;font-size:.9rem;
  color:var(--color-primary-darker);font-weight:600;
}
.guarantee svg{width:20px;height:20px;flex:none;color:var(--color-primary);margin-top:.12rem}
.guarantee b{display:block;color:var(--color-ink);font-weight:800;margin-bottom:.15rem}
.price-fineprint{
  display:flex;gap:.6rem;align-items:flex-start;margin:0 0 1.25rem;
  padding:.8rem .9rem;border-radius:var(--radius);
  background:var(--color-surface);border:1px solid var(--color-border);
  font-size:.86rem;line-height:1.5;color:var(--color-text-muted);
}
.price-fineprint svg{width:17px;height:17px;flex:none;color:var(--color-primary);margin-top:.15rem}
.price-fineprint strong{color:var(--color-ink)}

/* ---------------- Ecosystem ---------------- */
.eco{
  background:linear-gradient(160deg,var(--color-dark-2),var(--color-dark));
  border:1px solid rgb(45 161 219 / .28);border-radius:var(--radius-lg);
  padding:clamp(1.4rem,3vw,1.9rem);display:flex;flex-direction:column;
  box-shadow:var(--shadow-md);
  transition:border-color .2s ease,transform .2s ease,box-shadow .2s ease;
}
.eco:hover{border-color:var(--color-accent);transform:translateY(-3px);box-shadow:var(--shadow-lg)}
.eco h3{color:#fff}
/* The global `strong{color:var(--color-ink)}` and `h2{color:var(--color-ink)}`
   rules were painting dark text on these dark surfaces (measured 1.21:1 and
   1.02:1 - effectively invisible). Inherit the surrounding colour instead. */
.eco strong,.eco b{color:inherit;font-weight:800}
.eco .icon-tile{background:rgb(45 161 219 / .16);border-color:rgb(45 161 219 / .38);color:#8fd3f4}
.eco h3{margin-bottom:.45rem;font-size:1.15rem}
.eco p{color:#b7c4d3;font-size:.95rem;margin-bottom:1.15rem}
.eco a.card-link{color:#8fd3f4;margin-top:auto}
.eco a.card-link:hover{color:#c6e9fa}

/* ---------------- FAQ ---------------- */
.faq{max-width:820px;margin-inline:auto;display:grid;gap:.75rem}
details.qa{background:#fff;border:1px solid var(--color-border);border-radius:var(--radius);overflow:hidden}
details.qa[open]{border-color:var(--color-accent);box-shadow:var(--shadow-sm)}
details.qa summary{
  list-style:none;cursor:pointer;padding:1.05rem 3.25rem 1.05rem 1.25rem;position:relative;
  font-weight:700;color:var(--color-ink);font-size:1.02rem;min-height:44px;
}
details.qa summary::-webkit-details-marker{display:none}
details.qa summary::after{
  content:"";position:absolute;right:1.15rem;top:50%;transform:translateY(-50%);
  width:22px;height:22px;background:var(--color-primary);border-radius:50%;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E") center/13px no-repeat;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E") center/13px no-repeat;
  transition:transform .2s ease;
}
details.qa[open] summary::after{
  transform:translateY(-50%) rotate(135deg);
}
details.qa summary:hover{background:var(--color-surface)}
.qa-body{padding:0 1.25rem 1.2rem;color:var(--color-text-muted)}
.qa-body strong{color:var(--color-ink)}
.qa-body p:last-child{margin-bottom:0}

/* ---------------- Contact ---------------- */
.contact-grid{display:grid;gap:clamp(2rem,5vw,3.25rem);align-items:start}
@media (min-width:960px){.contact-grid{grid-template-columns:.9fr 1.1fr}}
.contact-list{list-style:none;display:grid;gap:1.1rem;margin:1.5rem 0 0}
.contact-list li{display:flex;gap:.85rem;align-items:flex-start}
.contact-list svg{width:22px;height:22px;color:var(--color-accent);flex:none;margin-top:.2rem}
.contact-list a{
  color:#fff;font-weight:700;
  display:inline-flex;align-items:center;min-height:44px;
  text-decoration:underline;text-decoration-color:rgb(255 255 255 / .45);text-underline-offset:4px;
}
.contact-list a:hover{color:var(--color-accent);text-decoration-color:var(--color-accent)}
.contact-list span{display:block;font-size:.88rem;color:#a8c0d6;line-height:1.45}
/* Emphasis on a navy panel has to go lighter, not darker. */
.contact-list strong{color:#8fd3f4;font-weight:700}

.form-card{background:#fff;border-radius:var(--radius-lg);padding:clamp(1.4rem,3.5vw,2.25rem);box-shadow:var(--shadow-lg);color:var(--color-text)}
/* This card is a white surface inside .section--dark, so undo the white-heading
   rule that would otherwise render the heading invisible. */
.section--dark .form-card h3{color:var(--color-ink)}
.form-card h3{margin-bottom:.35rem}
.form-card .stars{color:var(--color-star)}
.form-intro{font-size:.94rem;color:var(--color-text-muted);margin-bottom:1.5rem}
.field-row{display:grid;gap:1rem;grid-template-columns:1fr}
@media (min-width:560px){.field-row{grid-template-columns:1fr 1fr}}
.field{display:grid;gap:.35rem;margin-bottom:1rem}
.field label{font-weight:700;font-size:.9rem;color:var(--color-ink)}
.field .req{color:#b3261e}
.field .hint{font-size:.8rem;color:var(--color-text-muted);font-weight:500}
.field input,.field select,.field textarea{
  font:inherit;font-size:1rem;color:var(--color-text);
  padding:.8rem .9rem;min-height:50px;
  border:1px solid var(--color-border-strong);border-radius:var(--radius-sm);
  background:#fff;width:100%;
  transition:border-color .15s ease,box-shadow .15s ease;
}
.field textarea{min-height:118px;resize:vertical}
.field select{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234f5d6e' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right .8rem center;background-size:18px;padding-right:2.6rem}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--color-primary);box-shadow:0 0 0 3px rgb(45 161 219 / .25);outline:none}
.field input::placeholder,.field textarea::placeholder{color:#7b8798}
.consent{display:flex;gap:.7rem;align-items:flex-start;font-size:.85rem;color:var(--color-text-muted);margin:.25rem 0 1.35rem}
.consent{cursor:pointer;padding:.4rem 0}
.consent input{width:24px;height:24px;min-height:0;flex:none;margin-top:.05rem;accent-color:var(--color-primary);cursor:pointer}
.form-foot{font-size:.83rem;color:var(--color-text-muted);margin:.9rem 0 0;text-align:center}
.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}
.hp{position:absolute;left:-9999px}

/* ---------------- Footer ---------------- */
.site-footer{background:var(--color-dark);color:#a8c0d6;padding-block:clamp(2.5rem,6vw,4rem) 0;font-size:.95rem}
.footer-grid{display:grid;gap:2rem;grid-template-columns:1fr}
@media (min-width:700px){.footer-grid{grid-template-columns:1.4fr 1fr 1fr}}
@media (min-width:1024px){.footer-grid{grid-template-columns:1.5fr 1fr 1fr 1fr}}
.site-footer .brand{color:#fff}
.site-footer .brand:hover{color:var(--color-accent)}
.site-footer .brand-mark{background:#fff;color:var(--color-primary-darker)}
.site-footer .brand-sub{color:#8b9aab}
.footer-about{margin:1.1rem 0 1.35rem;max-width:38ch;color:#a9b6c5}
.footer-h{color:#fff;font-family:var(--font-display);font-size:.78rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;margin:0 0 1rem}
.footer-list{list-style:none;display:grid}
.footer-list a,.footer-list button{
  color:#a9b6c5;text-decoration:none;
  display:inline-flex;align-items:center;min-height:44px;
  background:none;border:0;padding:0;font:inherit;text-align:left;cursor:pointer;
}
.footer-list a:hover,.footer-list button:hover{color:#fff;text-decoration:underline}
address.nap{font-style:normal;display:grid;gap:.65rem}
address.nap a{color:#fff;text-decoration:none;font-weight:700;display:inline-flex;align-items:center;min-height:44px}
address.nap a:hover{color:var(--color-accent);text-decoration:underline;text-underline-offset:4px}
.socials{list-style:none;display:flex;gap:.6rem;margin-top:1.35rem}
/* GBP lead magnet, sitting directly under the social icons. */
.footer-lead{margin-top:1.1rem;max-width:22rem;font-size:.92rem;gap:.5rem}
.footer-lead svg{width:18px;height:18px;flex:none}
.socials a{
  width:44px;height:44px;border-radius:11px;display:grid;place-items:center;
  background:rgb(255 255 255 / .08);color:#fff;border:1px solid rgb(255 255 255 / .14);
}
.socials a:hover{background:var(--color-primary);border-color:var(--color-primary)}
.socials svg{width:20px;height:20px}
.footer-bar{
  margin-top:clamp(2.25rem,5vw,3rem);padding-block:1.35rem;
  border-top:1px solid rgb(255 255 255 / .13);
  display:flex;flex-wrap:wrap;gap:.6rem 1.5rem;align-items:center;justify-content:space-between;
  font-size:.86rem;color:#8b9aab;
}
.footer-bar ul{list-style:none;display:flex;flex-wrap:wrap;gap:0 1.25rem}
.footer-bar a{color:#8b9aab;text-decoration:none;display:inline-flex;align-items:center;min-height:44px}
.footer-bar a:hover{color:#fff;text-decoration:underline}

/* ---------------- Mobile sticky CTA ---------------- */
.mobile-cta{
  position:fixed;left:0;right:0;bottom:0;z-index:90;
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:.6rem;
  padding:.6rem clamp(.75rem,3vw,1rem);
  padding-bottom:calc(.6rem + env(safe-area-inset-bottom));
  background:rgb(255 255 255 / .96);backdrop-filter:blur(10px);
  border-top:1px solid var(--color-border);box-shadow:0 -6px 20px rgb(2 2 2 / .1);
}
.mobile-cta .btn{min-height:50px;font-size:.95rem;padding-inline:.75rem;white-space:nowrap}
/* At 320px "Free consultation" wrapped to two lines and pushed the bar to 68px.
   Shrink type/padding rather than truncate the label. */
@media (max-width:400px){
  .mobile-cta{gap:.45rem;padding-inline:.55rem}
  .mobile-cta .btn{font-size:.85rem;padding-inline:.4rem;gap:.35rem}
  .mobile-cta .btn svg{width:16px;height:16px}
}
@media (max-width:340px){
  .mobile-cta .btn{font-size:.78rem;padding-inline:.25rem}
}
@media (min-width:1024px){.mobile-cta{display:none}}
@media (max-width:1023px){body{padding-bottom:82px}}
/* The LeadConnector chat bubble is fixed bottom-right and would land underneath
   the sticky CTA bar on phones. Lift it clear. Selector list is best-effort
   across widget versions — verify on a real phone after launch. */
/* ---- Mobile bottom stack ----
   Three fixed things competed for the same corner: the sticky CTA bar, the GHL
   chat bubble (bottom-right) and the Vapi launcher (bottom-left). They now share
   one offset variable, and the bar hides while you scroll down so the widgets
   drop to a natural resting height instead of hovering mid-screen. */
:root{--bottom-ui:0px}
@media (max-width:1023px){
  :root{--bottom-ui:82px}
  .mobile-cta{transition:transform .25s ease}
  body.cta-hidden .mobile-cta{transform:translateY(110%)}
  body.cta-hidden{--bottom-ui:0px}
  chat-widget,#chat-widget,[id^="lc_text-widget"],.lc_text-widget,
  #lc_chat_widget,.chat-widget-container{
    bottom:calc(var(--bottom-ui) + 14px) !important;
    transition:bottom .25s ease;
  }
}

/* ---------------- Lead magnet modal ---------------- */
.modal{
  position:fixed;inset:0;z-index:150;display:none;
  align-items:flex-start;justify-content:center;padding:1rem;
  background:rgb(2 2 2 / .68);
}
.modal.is-open{display:flex}
.modal-panel{
  position:relative;background:#fff;border-radius:var(--radius-lg);
  width:100%;max-width:520px;max-height:calc(100dvh - 2rem);overflow:auto;
  overscroll-behavior:contain;margin-block:auto;
  box-shadow:var(--shadow-lg);
}
.modal-top{background:var(--color-dark);color:#fff;padding:1.5rem 1.5rem 1.35rem;border-radius:var(--radius-lg) var(--radius-lg) 0 0;position:relative;overflow:hidden}
.modal-top::after{content:"";position:absolute;inset:0;background:radial-gradient(420px 200px at 100% 0,rgb(45 161 219 / .4),transparent 65%);pointer-events:none}
.modal-top > *{position:relative;z-index:1}
.modal-top p.modal-kicker{display:inline-flex;align-items:center;gap:.45rem;font-family:var(--font-display);font-size:.7rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:#8fd3f4;background:rgb(45 161 219 / .16);border:1px solid rgb(45 161 219 / .4);border-radius:999px;padding:.3rem .7rem;margin-bottom:.85rem}
.modal-kicker svg{width:14px;height:14px}
.modal-top h2,#leadTitle{color:#fff;font-size:clamp(1.3rem,1.1rem + 1vw,1.6rem);margin-bottom:.5rem}
.modal-top p{color:#c3d0de;font-size:.94rem;margin:0}
.modal-body{padding:1.4rem 1.5rem 1.6rem}
.modal-close{
  position:absolute;top:.65rem;right:.65rem;z-index:2;
  width:44px;height:44px;border-radius:50%;border:1px solid rgb(255 255 255 / .3);
  background:rgb(2 2 2 / .35);color:#fff;cursor:pointer;display:grid;place-items:center;
}
.modal-close:hover{background:rgb(2 2 2 / .6)}
.modal-close svg{width:20px;height:20px}
.modal-get{list-style:none;display:grid;gap:.55rem;margin:0 0 1.25rem}
.modal-get li{position:relative;padding-left:1.7rem;font-size:.93rem;color:var(--color-text-muted)}
.modal-get li::before{
  content:"";position:absolute;left:0;top:.32em;width:17px;height:17px;background:var(--color-primary);border-radius:50%;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center/11px no-repeat;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center/11px no-repeat;
}
.modal-decline{display:block;width:100%;margin-top:.9rem;background:none;border:0;font:inherit;font-size:.85rem;color:var(--color-text-muted);text-decoration:underline;cursor:pointer;padding:.6rem;min-height:44px}
.modal-decline:hover{color:var(--color-ink)}
body.modal-open{overflow:hidden}

/* ---------------- Reveal animation ----------------
   Scoped to .js so that if scripting fails the page renders fully visible
   instead of mostly blank. The .js class is set by an inline script in <body>. */
.js .reveal{opacity:0;transform:translateY(18px);transition:opacity .55s ease,transform .55s ease}
.js .reveal.is-in{opacity:1;transform:none}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;scroll-behavior:auto !important}
  .btn:hover,.card:hover,.eco:hover,.price:hover{transform:none}
  .js .reveal{opacity:1;transform:none}
}
@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}

@media print{
  .site-header,.mobile-cta,.modal,.hero::after{display:none !important}
  body{padding-bottom:0}
}

/* ---------------- Blog ---------------- */
/* Same treatment as the homepage hero — two radial washes over the navy ramp —
   so inner pages read as the same site rather than a plainer variant. */
.post-hero{
  background:
    radial-gradient(920px 540px at 86% -10%, rgb(43 148 251 / .55), transparent 60%),
    radial-gradient(760px 500px at 4% 108%, rgb(45 161 219 / .38), transparent 60%),
    linear-gradient(158deg,#0d2a50 0%,#061b3d 52%,#0a2547 100%);
  color:#c8dcea;
  padding-block:clamp(2.25rem,5vw,3.75rem);position:relative;overflow:hidden}
.post-hero::after{content:"";position:absolute;inset:0;opacity:.35;pointer-events:none;
  background-image:linear-gradient(rgb(255 255 255 / .05) 1px,transparent 1px),linear-gradient(90deg,rgb(255 255 255 / .05) 1px,transparent 1px);
  background-size:52px 52px;mask-image:radial-gradient(60% 60% at 50% 40%,#000,transparent)}
.post-hero .container{position:relative;z-index:1}
.post-hero h1{color:#fff;max-width:22ch;font-size:clamp(1.8rem,1.3rem + 2.4vw,3rem)}
.crumbs{list-style:none;display:flex;flex-wrap:wrap;gap:.4rem .55rem;font-size:.85rem;margin:0 0 1.1rem;
  font-family:var(--font-display);letter-spacing:.04em;text-transform:uppercase;font-size:.72rem;font-weight:700}
.crumbs a{color:var(--color-accent);text-decoration:none}
.crumbs a:hover{text-decoration:underline}
.crumbs li[aria-current]{color:#93a4b6}
.post-meta{display:flex;flex-wrap:wrap;gap:.4rem .95rem;font-size:.9rem;color:#a8c0d6;margin:1.1rem 0 0;
  padding-top:1.1rem;border-top:1px solid rgb(255 255 255 / .15);align-items:center}
.post-meta svg{width:16px;height:16px;color:var(--color-accent)}
.post-meta span{display:inline-flex;align-items:center;gap:.4rem}
.post-layout{display:grid;gap:clamp(2rem,5vw,3.5rem);align-items:start}
@media (min-width:1000px){.post-layout{grid-template-columns:minmax(0,1fr) 300px}}
.prose{max-width:72ch;font-size:1.05rem;line-height:1.75;color:var(--color-text)}
.prose h2{margin:2.1em 0 .7em;font-size:clamp(1.4rem,1.2rem + .9vw,1.85rem);scroll-margin-top:90px}
.prose h3{margin:1.7em 0 .55em;font-size:clamp(1.1rem,1.03rem + .45vw,1.3rem);scroll-margin-top:90px}
.prose h4{margin:1.5em 0 .5em;font-size:1.05rem}
.prose h2:first-child,.prose h3:first-child{margin-top:0}
.prose p{margin:0 0 1.15em}
.prose ul,.prose ol{margin:0 0 1.3em;padding-left:1.35em}
.prose li{margin-bottom:.5em}
.prose ul{list-style:disc}
.prose ol{list-style:decimal}
.prose a{color:var(--color-primary);text-decoration:underline;text-underline-offset:3px}
.prose a:hover{color:var(--color-primary-dark)}
/* A .btn inside .prose was losing its colour: `.prose a` (0,1,1) outranks
   `.btn--primary` (0,1,0), so buttons rendered brand-blue text on a brand-blue
   fill — invisible. Restate each variant at matching specificity. */
.prose a.btn{text-decoration:none}
.prose a.btn:hover{text-decoration:none}
.prose a.btn--primary,.prose a.btn--primary:hover{color:#fff}
.prose a.btn--on-dark,.prose a.btn--on-dark:hover{color:var(--color-primary-darker)}
.prose a.btn--outline-dark,.prose a.btn--outline-dark:hover{color:#fff}
.prose a.btn--ghost{color:var(--color-primary)}
.prose strong{color:var(--color-ink)}
.prose blockquote{margin:1.6em 0;padding:1rem 1.25rem;border-left:4px solid var(--color-accent);
  background:var(--color-surface);border-radius:0 var(--radius) var(--radius) 0;color:var(--color-text-muted)}
.prose blockquote p:last-child{margin-bottom:0}
.prose table{width:100%;border-collapse:collapse;margin:1.5em 0;font-size:.95rem;display:block;overflow-x:auto}
.prose th,.prose td{border:1px solid var(--color-border);padding:.6rem .75rem;text-align:left}
.prose th{background:var(--color-surface);font-weight:700;color:var(--color-ink)}
.sidebar{position:sticky;top:90px;display:grid;gap:1.15rem}
.toc{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:1.25rem}
.toc h2{font-family:var(--font-display);font-size:.74rem;font-weight:800;letter-spacing:.13em;
  text-transform:uppercase;color:var(--color-text-muted);margin:0 0 .85rem}
.toc ol{list-style:none;display:grid;gap:.5rem;margin:0;padding:0;counter-reset:toc}
.toc a{display:block;font-size:.92rem;color:var(--color-text);text-decoration:none;line-height:1.4;
  padding-left:1.5rem;position:relative;min-height:28px}
.toc a::before{counter-increment:toc;content:counter(toc);position:absolute;left:0;top:0;
  font-family:var(--font-display);font-size:.72rem;font-weight:800;color:var(--color-accent)}
.toc a:hover{color:var(--color-primary-dark);text-decoration:underline}
.cta-card{background:linear-gradient(160deg,var(--color-dark-2),var(--color-dark));
  border:1px solid rgb(45 161 219 / .3);border-radius:var(--radius-lg);padding:1.5rem;box-shadow:var(--shadow-md)}
.cta-card h2{color:#fff;font-size:1.15rem;margin:0 0 .5rem}
.cta-card p{color:#b7c4d3;font-size:.93rem;margin:0 0 1.15rem}
/* Blog index */
.post-grid{display:grid;gap:1.15rem;grid-template-columns:1fr}
@media (min-width:640px){.post-grid{grid-template-columns:repeat(2,1fr)}}
@media (min-width:1024px){.post-grid{grid-template-columns:repeat(3,1fr)}}
/* Index thumbnails. Decorative — the card's heading is the real link and carries
   the accessible name, so the image is aria-hidden and out of the tab order. */
.post-card-img{
  display:block;overflow:hidden;background:var(--color-dark);
  /* pull out past the card's own padding so the image sits flush to the edges */
  --pad:clamp(1.25rem,3vw,1.6rem);
  margin:calc(var(--pad) * -1) calc(var(--pad) * -1) 1.1rem;
  border-radius:calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
}
.post-card-img img{display:block;width:100%;height:auto;aspect-ratio:1200/630;object-fit:cover;transition:transform .3s ease}
.post-card:hover .post-card-img img{transform:scale(1.03)}
@media (prefers-reduced-motion:reduce){.post-card-img img{transition:none}.post-card:hover .post-card-img img{transform:none}}
.post-card{background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-lg);
  padding:clamp(1.25rem,3vw,1.6rem);display:flex;flex-direction:column;box-shadow:var(--shadow-sm);
  transition:box-shadow .2s ease,transform .2s ease,border-color .2s ease}
.post-card:hover{box-shadow:var(--shadow-md);transform:translateY(-3px);border-color:var(--color-accent)}
.post-card h2{font-size:1.1rem;margin:0 0 .55rem;line-height:1.3}
.post-card h2 a{color:var(--color-ink);text-decoration:none}
.post-card h2 a:hover{color:var(--color-primary-dark)}
.post-card p{color:var(--color-text-muted);font-size:.93rem;margin:0 0 1.1rem}
.post-card .pm{margin-top:auto;display:flex;flex-wrap:wrap;gap:.3rem .8rem;font-size:.8rem;
  color:var(--color-text-muted);font-weight:600}
.blog-search{display:grid;gap:.6rem;max-width:520px;margin:0 auto 2rem}
.blog-search input{font:inherit;font-size:1rem;padding:.85rem 1rem;min-height:50px;
  border:1px solid var(--color-border-strong);border-radius:var(--radius-sm);width:100%}
.blog-search input:focus{border-color:var(--color-primary);box-shadow:0 0 0 3px rgb(45 161 219 / .25);outline:none}
.blog-count{text-align:center;font-size:.9rem;color:var(--color-text-muted);margin:0 0 1.5rem}
.no-results{text-align:center;padding:2.5rem 1rem;color:var(--color-text-muted)}


/* ---------------- Booking CTA + calendar modal ---------------- */
.booking-cta{
  display:flex;flex-wrap:wrap;gap:1.5rem;align-items:center;justify-content:space-between;
  background:linear-gradient(160deg,var(--color-dark-2),var(--color-dark));
  border:1px solid rgb(45 161 219 / .3);border-radius:var(--radius-lg);
  padding:clamp(1.4rem,3.5vw,2rem);box-shadow:var(--shadow-md);
  margin:0 0 clamp(2rem,5vw,3rem);
}
.booking-cta-copy{flex:1 1 24rem;min-width:0}
.booking-cta-copy h2{margin-bottom:.45rem}
.booking-cta-copy .lede{margin:0}
.booking-cta .btn{flex:none}

.modal--wide .modal-panel{max-width:760px}
.modal--wide .modal-panel--book{max-width:720px}
.modal-panel--book{-webkit-overflow-scrolling:touch}
.booking-frame{background:#fff;padding:clamp(.4rem,1.5vw,.9rem)}
.booking-frame iframe{display:block;width:100%;border:0;min-height:760px;border-radius:var(--radius)}
@media (max-width:600px){.booking-frame iframe{min-height:900px}}

/* The booking widget grows past its starting height on the details step. The
   panel scrolls to reach it, and the iframe is no longer scrolling="no", so the
   submit button is always reachable even if the resize message never lands. */
.booking-frame{position:relative;min-height:320px}
.booking-frame iframe{opacity:0;transition:opacity .25s ease}
.booking-ready .booking-frame iframe{opacity:1}
.booking-loading{
  position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:.85rem;text-align:center;
  padding:2rem 1.5rem;margin:0;color:var(--color-text-muted);font-weight:600;
}
.booking-loading small{display:block;max-width:34ch;font-weight:400;font-size:.86rem;color:var(--color-text-muted)}
.booking-spinner{
  width:34px;height:34px;border-radius:50%;
  border:3px solid var(--color-border);border-top-color:var(--color-primary);
  animation:book-spin .8s linear infinite;
}
@keyframes book-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){
  .booking-spinner{animation:none;border-top-color:var(--color-border)}
  .booking-frame iframe{transition:none}
}

/* ---------------- GBP audit launch panel ----------------
   Not an iframe. The tool's shell allows framing, but it boots into a SPA that
   navigates to a host which refuses — the frame stayed empty (0 bytes) in every
   real browser. It opens in a new tab instead, which suits a multi-step tool
   better than a fixed-height window anyway. */
.audit-launch{
  display:grid;gap:clamp(1.5rem,4vw,2.5rem);align-items:center;
  background:linear-gradient(150deg,var(--color-dark-2),var(--color-dark));
  border:1px solid rgb(45 161 219 / .32);border-radius:var(--radius-lg);
  padding:clamp(1.6rem,4vw,2.75rem);box-shadow:var(--shadow-md);
}
@media (min-width:860px){.audit-launch{grid-template-columns:minmax(0,1.35fr) minmax(0,.65fr)}}
.audit-launch h2{color:#fff;font-size:clamp(1.4rem,1.15rem + 1.2vw,1.95rem);margin:0 0 .55rem}
.audit-launch p{color:#c3d0de;margin:0 0 1.4rem;max-width:52ch}
.audit-kicker{
  display:inline-flex;align-items:center;gap:.45rem;
  font-family:var(--font-display);font-size:.7rem;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;color:#8fd3f4;background:rgb(45 161 219 / .16);
  border:1px solid rgb(45 161 219 / .4);border-radius:999px;padding:.3rem .7rem;margin:0 0 .9rem;
}
.audit-launch-note{font-size:.86rem;color:#93a4b6;margin:.9rem 0 0}
/* ---- Embedded audit tool ----------------------------------------------------
   The tool sends both `X-Frame-Options: SAMEORIGIN` and
   `Content-Security-Policy: frame-ancestors *`. Where both are present the CSP
   wins and XFO is ignored, which is why this frames now where an earlier
   attempt was refused. If the provider ever drops the CSP the frame goes blank,
   so the escape-hatch link below it is not optional — it is the fallback path
   and it keeps the click tracked. */
.audit-embed{
  margin-top:clamp(1.25rem,3vw,1.75rem);
  background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-md);overflow:hidden;
}
.audit-embed iframe{
  display:block;width:100%;border:0;
  height:clamp(620px,78vh,860px);
}
.audit-embed-foot{
  display:flex;flex-wrap:wrap;align-items:center;gap:.5rem 1rem;justify-content:space-between;
  padding:.85rem clamp(.9rem,2.5vw,1.35rem);
  border-top:1px solid var(--color-border);background:var(--color-surface);
  font-size:.88rem;color:var(--color-text-muted);margin:0;
}
.audit-embed-foot a{font-weight:700}
.audit-steps{
  list-style:none;display:grid;gap:.7rem;margin:0;padding:0;
  border-left:1px solid rgb(255 255 255 / .14);padding-left:clamp(1.1rem,3vw,1.75rem);
}
@media (max-width:859px){
  .audit-steps{border-left:0;border-top:1px solid rgb(255 255 255 / .14);padding-left:0;padding-top:1.35rem}
}
.audit-steps li{
  display:flex;align-items:center;gap:.7rem;color:#c3d0de;font-size:.94rem;font-weight:600;
}
.audit-steps span{
  flex:none;width:26px;height:26px;border-radius:8px;display:grid;place-items:center;
  background:rgb(45 161 219 / .18);border:1px solid rgb(45 161 219 / .4);
  color:#8fd3f4;font-weight:800;font-size:.82rem;
}

/* ---------------- Embedded GoHighLevel form (lead modal) ----------------
   Its height is not known up front and it grows as GHL validates, so the frame
   is sized from the height GHL posts and can scroll if a message is ever
   missed. No scrolling="no" here — that is what hid the calendar's submit
   button. */
/* A LOADING floor only: it reserves room for the spinner so the panel does not
   jump as the widget arrives, and .form-ready releases it the moment the real
   height lands. Without that release a form measuring shorter than the floor
   leaves the difference as dead white space under it, because the frame shrinks
   to fit and the wrapper does not.

   Keep this number modest. Raising it to 556px to match the height GHL's embed
   declares was a mistake twice over: it widened that gap before the release
   existed, and the matching data-height on the iframe let GHL's form_embed.js
   pin the frame at 556px, putting the surplus INSIDE the frame where no rule
   here can reach it. The frame's real height comes from the widget's own
   postMessage — nothing should be declaring one. */
.ghl-form{position:relative;min-height:430px}
.ghl-form iframe{
  display:block;width:100%;height:430px;border:0;border-radius:var(--radius);
  background:#fff;opacity:0;transition:opacity .25s ease;
}
.form-ready .ghl-form{min-height:0}
.form-ready .ghl-form iframe{opacity:1}
.ghl-form-loading{
  position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:.8rem;margin:0;
  color:var(--color-text-muted);font-weight:600;font-size:.92rem;
}
@media (prefers-reduced-motion:reduce){.ghl-form iframe{transition:none}}

/* ---------------- Google profile widgets ----------------
   Four cross-origin iframes from the profile manager: reviews, Google Q&A,
   photos and posts. Their contents index to the widget host, not here, so the
   site's own testimonials and FAQ copy stay in the page alongside them — these
   are for freshness, not for crawlable text.

   Every one sits on an explicit white panel. Two of the four render on white
   with no colour parameters available, and #reviews is a dark section, so
   without the panel the frame would cut a white rectangle out of the navy. */
.gw-block{margin-top:2rem}
.gw-label{
  font-family:var(--font-display);font-size:.72rem;font-weight:800;
  letter-spacing:.12em;text-transform:uppercase;
  color:var(--color-text-muted);margin:0 0 .7rem;text-align:center;
}
.on-dark .gw-label{color:#8fd3f4}
.gw{
  background:#fff;border-radius:var(--radius);overflow:hidden;
  box-shadow:var(--shadow-sm);
}
.gw iframe{display:block;width:100%;border:0}
/* Heights are the vendor's, and they are fixed: the frames are cross-origin so
   their real content height cannot be measured from here. Narrow screens get
   more room because the same content reflows taller. */
/* These are FALLBACKS. Each frame carries scrolling="no", so a frame shorter
   than its content would clip rather than scroll — the auto-size listener in
   index.html resizes any frame whose widget reports a height, and these
   numbers only apply when one stays silent. Generous on purpose: dead space
   under a widget is survivable, a clipped last row is not. */
/* Reviews fall back to the vendor's own 300px rather than a generous guess:
   this frame is a single row of cards, so an over-tall fallback reads as a
   white band under the reviews instead of as breathing room. Photos and posts
   are grids of unknown length, where clipping is the worse failure. */
.gw--reviews iframe{min-height:300px}
.gw--images iframe{min-height:760px}
.gw--posts iframe{min-height:700px}
@media (max-width:640px){
  .gw--images iframe{min-height:640px}
  .gw--posts iframe{min-height:620px}
}

/* ---------------- Form status + success state ---------------- */
.form-status{
  margin:0 0 1rem;padding:.75rem .9rem;border-radius:var(--radius);
  font-size:.92rem;font-weight:600;line-height:1.45;
}
.form-status--error{background:#fdecec;border:1px solid #f3b8b8;color:#8a1c1c}
.form-done{
  display:flex;flex-direction:column;align-items:flex-start;gap:.6rem;
  padding:1.6rem 0;outline:none;
}
.form-done svg{width:44px;height:44px;flex:none;color:var(--color-primary);
  background:var(--color-accent-soft);border-radius:50%;padding:9px}
.form-done h3{margin:.2rem 0 0;font-size:1.2rem}
.form-done p{margin:0;color:var(--color-text-muted);font-size:.95rem}
/* inside the dark lead modal the same block needs light ink */
.modal .form-done h3{color:var(--color-ink)}

/* ---------------- Lead magnet (wider, split layout) ---------------- */
.modal--wide .modal-panel--lead{max-width:880px}
.modal-body--split{display:grid;gap:clamp(1.25rem,3vw,2rem)}
@media (min-width:720px){
  .modal-body--split{grid-template-columns:1.05fr .95fr;align-items:start}
}

/* Below the two-column breakpoint the form was LAST, under the header, four
   benefit bullets and a stats row. Measured on a 390x844 phone: 1,519px of
   content in an 812px panel, which put the form itself roughly 700px below the
   fold — the one thing the modal exists to show, reachable only by scrolling
   inside it. The form comes first on a phone; the sell sits underneath for
   anyone who wants it. */
@media (max-width:719px){
  .modal-body--split .lead-form{order:-1}
  /* The header eats ~250px of an 812px panel before the form starts. */
  .modal-top{padding:1.15rem 1.15rem 1rem}
  .modal-top h2,#leadTitle{font-size:1.18rem}
  .modal-top p#leadDesc{font-size:.88rem}
  .modal-kicker{margin-bottom:.6rem}
  .modal-body{padding:1.15rem 1.15rem 1.35rem}
}

/* The close button is 44px at top:.65rem/right:.65rem and sat directly on top
   of the kicker pill, clipping "NO CARD, NO CALL" on a narrow screen. Keep the
   header text clear of it at every width. */
.modal-top{padding-right:3.6rem}

/* The panel itself was the scroll container, so the header scrolled away with
   everything else — and the close button lives inside the header. On a 720px
   laptop 210px of a 911px modal sits below the fold, and on a phone 672px
   does, which meant the way out scrolled off exactly when someone wanted it.

   Pin the header and scroll the body instead. The form is tall enough
   (556px + footnotes) that no sensible layout fits it under a header on a
   short screen, so scrolling is the right answer — it just needs to leave the
   exit where people can reach it. Escape and a backdrop click already worked;
   now the X does too, at any scroll position. */
.modal-panel--lead{display:flex;flex-direction:column;overflow:hidden}
.modal-panel--lead .modal-top{flex:0 0 auto}
.modal-panel--lead .modal-body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}
.lead-sub{font-family:var(--font-display);font-size:.72rem;font-weight:800;letter-spacing:.12em;
  text-transform:uppercase;color:var(--color-text-muted);margin:0 0 .85rem}
.lead-results{margin-top:1.35rem;padding-top:1.15rem;border-top:1px solid var(--color-border)}
.lead-results-h{font-size:.8rem;font-weight:700;color:var(--color-text-muted);margin:0 0 .7rem}
.lead-results ul{list-style:none;display:grid;grid-template-columns:repeat(3,1fr);gap:.6rem;margin:0}
.lead-results li{text-align:center;background:var(--color-surface);border:1px solid var(--color-border);
  border-radius:var(--radius);padding:.6rem .35rem}
.lead-results b{display:block;font-size:clamp(1.1rem,1rem + .6vw,1.4rem);font-weight:800;
  letter-spacing:-.03em;color:var(--color-primary-darker);line-height:1.1}
.lead-results span{display:block;font-size:.72rem;color:var(--color-text-muted);line-height:1.25;margin-top:.15rem}
/* Marks a field as optional, the counterpart to .req. Only name, phone and
   email are required now — functions/api/lead.js enforces name and email
   alone, so the rest was front-end friction the server never asked for. */
.field .opt{font-weight:500;font-size:.82em;color:var(--color-text-muted)}
.lead-form .field{margin-bottom:.85rem}

/* ---------------- Service-area map (inline SVG, no tiles) ---------------- */
.map-card{margin:0;padding:0;background:#fff;border:1px solid var(--color-border);
  border-radius:var(--radius-lg);box-shadow:var(--shadow-md);overflow:hidden}
.map-card::after{content:none}
.map-canvas{background:linear-gradient(170deg,#f6fbff,#e9f4fd);padding:clamp(.5rem,2vw,1rem)}
.svgmap{display:block;width:100%;height:auto;margin-inline:auto}
.m-area{fill:url(#areaFill);stroke:var(--color-primary);stroke-width:2;stroke-dasharray:7 5;stroke-linejoin:round}
.m-water{fill:#a8d5ef;stroke:#7cb8dd;stroke-width:1}
.m-water-label{fill:#2e6f96;font-size:14px;font-weight:600;text-anchor:middle;
  font-family:var(--font-sans);pointer-events:none}
.m-hwy{fill:none;stroke:#b9c6d3;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.m-hwy--major{stroke:#9aa9b8;stroke-width:3.4}
.m-dot{fill:#fff;stroke:var(--color-primary-dark);stroke-width:2}
.m-dot--town{stroke-width:2.6}
.m-label{font-family:var(--font-sans);font-weight:700;fill:var(--color-ink);
  paint-order:stroke;stroke:#fff;stroke-width:3.2;stroke-linejoin:round}
.m-label--town{font-size:19px}
.m-label--small{font-size:15px;font-weight:600;fill:var(--color-text-muted)}
.m-label--office{font-size:20px;fill:var(--color-primary-darker);font-weight:800}
.m-office-halo{fill:var(--color-accent);opacity:.22}
.m-office-pin{fill:var(--color-primary);stroke:#fff;stroke-width:2.2}
.m-office-dot{fill:#fff}
/* The SVG renders ~25% smaller than its viewBox in the column, and ~50% on a
   phone, so label sizes are set in viewBox units to stay legible after scaling.
   Below 640px the minor places are dropped rather than rendered unreadably —
   they are all still listed as text in the .towns list beside the map. */
@media (max-width:640px){
  .m-label--small,.m-water-label{display:none}
  .m-label--town{font-size:26px}
  .m-label--office{font-size:28px}
  .m-dot--small{r:4}
}
.map-legend{display:flex;flex-wrap:wrap;gap:.5rem 1.1rem;align-items:center;
  padding:.9rem clamp(.9rem,2.5vw,1.15rem);border-top:1px solid var(--color-border);
  font-size:.85rem;color:var(--color-text-muted);background:#fff}
.mk{display:inline-flex;align-items:center;gap:.45rem}
.mk::before{content:"";width:14px;height:14px;border-radius:4px;flex:none}
.mk--area::before{background:rgb(43 148 251 / .28);border:2px dashed var(--color-primary)}
.mk--office::before{background:var(--color-primary);border-radius:50%}
.mk-link{margin-left:auto;font-weight:700;text-decoration:none;display:inline-flex;
  align-items:center;gap:.35rem;min-height:44px}
.mk-link:hover{text-decoration:underline}
.mk-link svg{width:1em;height:1em}
@media (prefers-reduced-motion:no-preference){
  .m-office-halo{animation:pulse 2.8s ease-in-out infinite}
  @keyframes pulse{0%,100%{opacity:.18;r:13}50%{opacity:.32;r:17}}
}

/* ---------------- Legal pages ---------------- */
.prose.legal{font-size:1rem}
.prose.legal h2{font-size:clamp(1.2rem,1.1rem + .5vw,1.45rem);margin:2em 0 .6em}
.prose.legal p,.prose.legal li{color:var(--color-text-muted)}
.prose.legal strong{color:var(--color-ink)}
.post-hero .post-meta time{color:#fff;font-weight:700}

/* ---------------- Vapi voice demo ---------------- */
.ai-demo-note{display:flex;gap:.6rem;align-items:flex-start;
  font-size:.88rem;color:#93a4b6;margin:1.5rem 0 0;max-width:52ch}
.ai-demo-arrow{width:22px;height:22px;flex:none;color:var(--color-accent);margin-top:.05rem}
@media (prefers-reduced-motion:no-preference){
  .ai-demo-arrow{animation:nudgeDL 2.4s ease-in-out infinite}
  @keyframes nudgeDL{
    0%,100%{transform:translate(0,0)}
    50%{transform:translate(-3px,3px)}
  }
}
.ai-demo-note a{color:var(--color-accent);text-decoration:underline;text-underline-offset:3px}
.ai-demo-note a:hover{color:#c6e9fa}
/* On phones the floating launcher lands under the sticky CTA bar (and sits opposite
   the GHL chat bubble, which is bottom-right). The SDK sets `position:fixed;
   bottom:1.5rem` as an INLINE style on a div inside .vapi-widget-wrapper, so the
   override has to target that child and use !important to beat the inline rule. */
@media (max-width:1023px){
  .vapi-widget-wrapper > div[style*="fixed"],
  vapi-widget .vapi-widget-wrapper > div{
    bottom:calc(var(--bottom-ui) + 14px) !important;
    transition:bottom .25s ease;
  }
}

/* ---------------- Related reading (post-to-post internal links) ---------------- */
/* Now lives inside the article container, so it lines up with the copy above
   and runs the full width instead of being capped at 72ch and hanging outside
   the page gutters. */
.related{margin:1.75rem 0 0;padding:1.4rem clamp(1.1rem,3vw,1.75rem);background:var(--color-surface);
  border:1px solid var(--color-border);border-radius:var(--radius-lg)}
@media (min-width:760px){
  .related ul{grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr));gap:.2rem 1.75rem}
}
.related h2{font-family:var(--font-display);font-size:.76rem;font-weight:800;letter-spacing:.13em;
  text-transform:uppercase;color:var(--color-text-muted);margin:0 0 .9rem}
.related ul{list-style:none;display:grid;gap:.2rem;margin:0;padding:0}
.related a{display:flex;align-items:center;gap:.6rem;min-height:44px;padding:.35rem 0;
  color:var(--color-primary);font-weight:600;text-decoration:none;line-height:1.35}
.related a::before{content:"";width:16px;height:16px;flex:none;background:var(--color-primary);
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E") center/14px no-repeat;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E") center/14px no-repeat}
.related a:hover{color:var(--color-primary-dark);text-decoration:underline}
