/* ==========================================================================
   Ronnie Trammell, Realtor - public design system
   Pro Star CMS Development. Mobile first. Navy and red taken from the
   shield logo and the crew shirts.
   ========================================================================== */

*,*::before,*::after{box-sizing:border-box}

/* Sticky header measurements. --nav-h is the design height of the white bar
   and drives its own min-height; --nav-off is what everything else offsets
   against and is re-measured by site.js so zoom, wrapped labels and the
   scrolled compact state all stay in step. Both have working defaults, so
   nothing here depends on JavaScript. */
:root{
  --nav-h:78px;
  --nav-h-stuck:66px;
  --nav-off:78px;
  --sticky-gap:14px;
  --menu-drop:18px;
}

html{
  -webkit-text-size-adjust:100%;
  /* Anchors and keyboard focus must land below the sticky bar, not under it. */
  scroll-padding-top:calc(var(--nav-off) + var(--sticky-gap));
}

/* Horizontal overflow is clipped on the ROOT, never on <body>. A body with
   overflow-x set becomes the scroll container for its descendants, and
   position:sticky on the header then has nothing to stick to. "clip" is used
   rather than "hidden" because it creates no scroll container at all. */
html{overflow-x:clip}
@supports not (overflow-x:clip){
  html{overflow-x:hidden}
}

body{
  margin:0;
  font-family:"Source Sans 3",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  font-size:1.0625rem;
  line-height:1.65;
  color:var(--ink);
  background:var(--paper);
}

/* Every heading or section that can be linked to keeps clear of the bar. */
[id]{scroll-margin-top:calc(var(--nav-off) + var(--sticky-gap))}

/* The drawer is open: freeze the page behind it so the menu is the only
   thing that scrolls. Set by site.js, removed again on close.

   ONLY on <html>. Putting overflow on <body> makes the body the scroll
   container for everything inside it, and .site-header would then be sticky
   against a box that never scrolls - it would slide off the top of the
   window the moment the drawer was opened. */
html.nav-open{overflow:hidden}
img,video{max-width:100%;height:auto;display:block}
h1,h2,h3,h4{
  font-family:"Barlow Condensed",Impact,"Arial Narrow",sans-serif;
  font-weight:700;
  line-height:1.08;
  letter-spacing:.005em;
  margin:0 0 .5em;
  color:var(--brand-deep);
  text-wrap:balance;
}
h1{font-size:clamp(2.35rem,6.2vw,4.1rem);text-transform:uppercase}
h2{font-size:clamp(1.85rem,4.2vw,2.9rem);text-transform:uppercase}
h3{font-size:clamp(1.25rem,2.2vw,1.55rem)}
h4{font-size:1.08rem}
p{margin:0 0 1.05em}
a{color:var(--brand);text-underline-offset:.18em}
a:hover{color:var(--accent)}
ul,ol{margin:0 0 1.05em;padding-left:1.15em}
li{margin:0 0 .35em}
strong{font-weight:700}
hr{border:0;border-top:1px solid var(--line);margin:2.5rem 0}
address{font-style:normal}

.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}

.skip-link{
  position:absolute;left:.5rem;top:-4rem;z-index:200;
  background:var(--accent);color:#fff;padding:.7rem 1.1rem;border-radius:0 0 6px 6px;
  font-weight:700;text-decoration:none;transition:top .16s ease;
}
.skip-link:focus{top:0;color:#fff}

:where(a,button,input,select,textarea,summary,[tabindex]):focus-visible{
  outline:3px solid var(--accent);
  outline-offset:2px;
  border-radius:3px;
}

.wrap{width:min(100% - 2rem,1220px);margin-inline:auto}
@media (min-width:900px){.wrap{width:min(100% - 4rem,1220px)}}

.section{padding:clamp(2.6rem,5vw,4.35rem) 0}
.section--tight{padding:clamp(2rem,3.2vw,2.8rem) 0}
.section--cream{background:var(--cream)}
.section--brand{background:var(--brand-deep);color:#e8f4fb}
.section--brand h2,.section--brand h3{color:#fff}
.section--brand a{color:#bce0f4}

.eyebrow{
  font-family:"Barlow Condensed",sans-serif;
  font-size:.94rem;font-weight:700;letter-spacing:.15em;text-transform:uppercase;
  color:var(--accent);margin:0 0 .55rem;
}
.section--brand .eyebrow,
.hero .eyebrow,
.page-hero .eyebrow,
.cta-band .eyebrow{color:#ff8ace}
.lede{font-size:clamp(1.09rem,1.7vw,1.28rem);color:var(--muted);max-width:64ch}
.section--brand .lede{color:#bfd9ea}
.measure{max-width:68ch}
.center{text-align:center}
.center .lede{margin-inline:auto}

/* --- Buttons ------------------------------------------------------------ */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.5rem;
  min-height:52px;padding:.85rem 1.6rem;
  font-family:"Barlow Condensed",sans-serif;
  font-size:1.16rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
  border:2px solid transparent;border-radius:4px;
  text-decoration:none;cursor:pointer;
  transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .12s ease;
}
.btn:active{transform:translateY(1px)}
/* Without this an inline icon inherits the SVG default box and swamps the
   label - which is what the old red telephone button was doing. */
.btn .ic{width:18px;height:18px;fill:currentColor;flex:none}
.btn-primary{background:var(--accent);border-color:var(--accent);color:#fff}
.btn-primary:hover{background:var(--accent-dark);border-color:var(--accent-dark);color:#fff}
.btn-brand{background:var(--brand);border-color:var(--brand);color:#fff}
.btn-brand:hover{background:var(--brand-deep);border-color:var(--brand-deep);color:#fff}
.btn-ghost{background:transparent;border-color:currentColor;color:var(--brand)}
.btn-ghost:hover{background:var(--brand);border-color:var(--brand);color:#fff}
.btn-on-dark{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.75);color:#fff}
.btn-on-dark:hover{background:#fff;border-color:#fff;color:var(--brand-deep)}
/* Small buttons are interface, not poster type. */
.btn-sm{
  min-height:44px;padding:.55rem 1.05rem;
  font-family:"Source Sans 3",sans-serif;
  font-size:.9rem;font-weight:700;letter-spacing:.045em;
}
.btn-block{width:100%}
.btn-row{display:flex;flex-wrap:wrap;gap:.85rem;margin-top:1.5rem}
.center .btn-row{justify-content:center}

/* --- Design language ----------------------------------------------------

   The things that stop this looking like a theme. All of it is drawn in CSS
   from the trade the business is actually in - the paper tag on a work order,
   the blade curling out of the shield, a mower stripe, a cut bed edge.
   Restrained on purpose: these are accents on a serious business, not
   decoration.
   --------------------------------------------------------------------- */

/* Section tag. Replaces the plain eyebrow. The notch on the left and the
   punched hole are the shape of a work ticket. */
.tag{
  display:inline-flex;align-items:center;gap:.5rem;
  margin:0 0 .85rem;padding:.34rem .85rem .34rem 1.5rem;
  position:relative;
  background:var(--cream);
  border:1px solid var(--line);
  border-radius:3px 4px 4px 3px;
  clip-path:polygon(0 50%,.55rem 0,100% 0,100% 100%,.55rem 100%);
  font-family:"Source Sans 3",sans-serif;
  font-size:.78rem;font-weight:700;letter-spacing:.13em;text-transform:uppercase;
  color:var(--brand);
}
.tag::before{
  content:"";position:absolute;left:.82rem;top:50%;transform:translateY(-50%);
  width:5px;height:5px;border-radius:50%;
  background:var(--accent);
}
.tag--onDark{background:rgba(255,255,255,.09);border-color:rgba(255,255,255,.24);color:#fff}
.tag--onDark::before{background:#ff5aba}
.tag--onAccent{background:rgba(0,0,0,.16);border-color:rgba(255,255,255,.3);color:#fff}
.tag--onAccent::before{background:#fff}

/* Hanger rule. The hook from the logo, drawn as a heading underline. Sits
   under a section heading instead of a generic centred divider. */
.blade-rule{
  display:block;width:132px;height:20px;margin:.1rem 0 1.15rem;
  color:var(--accent);flex:none;
}
/* The hook and its solid run are brand red; the stitched tail picks up the
   surrounding ink so it stays readable on pale and on dark panels alike. */
.blade-rule .hr-stripe{color:var(--brand);opacity:.6}
.method .blade-rule .hr-stripe,
.hero .blade-rule .hr-stripe,
.cta-band .blade-rule .hr-stripe{color:#fff;opacity:.75}
.center .blade-rule{margin-inline:auto}
.blade-rule svg{display:block;width:100%;height:100%;overflow:visible}

/* Stitching. A seam, used where a section needs a lid rather than a hard
   edge. Two dashes offset so it reads as thread, not as a dashed border. */
.stitch{
  position:relative;height:0;
  border-top:2px dashed rgba(18,48,110,.26);
}
.stitch::after{
  content:"";position:absolute;left:.6rem;right:.6rem;top:1px;
  border-top:2px dashed rgba(200,32,46,.28);
}
.section--brand .stitch,.stitch--onDark{border-top-color:rgba(255,255,255,.26)}
.stitch--onDark::after{border-top-color:rgba(255,138,147,.4)}

/* Pressed fold. A single soft crease of light across a dark panel, the way a
   pressed sleeve catches a room light. Never more than one per panel. */
.folded{position:relative;isolation:isolate}
.folded::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:
    linear-gradient(104deg,transparent 0 38%,rgba(255,255,255,.055) 38.4% 40%,transparent 40.4% 100%),
    linear-gradient(104deg,transparent 0 61%,rgba(255,255,255,.035) 61.3% 62.4%,transparent 62.8% 100%);
}

/* Woven ground. Barely-there fabric texture for pale sections. Kept under 3%
   so it registers as paper stock rather than as a pattern. */
.section--cream{
  background-color:var(--cream);
  background-image:
    repeating-linear-gradient(90deg,rgba(18,48,110,.022) 0 1px,transparent 1px 3px),
    repeating-linear-gradient(0deg,rgba(18,48,110,.022) 0 1px,transparent 1px 3px);
}

/* Framed photograph. An offset rule sits behind the picture and corner ticks
   sit over it, so a photo reads as placed rather than dropped in. */
.framed{position:relative;isolation:isolate}
.framed::before{
  content:"";position:absolute;z-index:-1;
  inset:1.15rem -1.15rem -1.15rem 1.15rem;
  border:2px solid rgba(18,48,110,.34);
  border-radius:4px;
}
.framed--accent::before{border-color:rgba(200,32,46,.42)}
.framed--onDark::before{border-color:rgba(255,255,255,.28)}
.framed > img,.framed > picture > img,.framed .frame-shot{
  position:relative;display:block;width:100%;border-radius:4px;
  box-shadow:0 20px 46px rgba(6,17,43,.26);
}
.framed::after{
  content:"";position:absolute;left:-6px;top:-6px;width:26px;height:26px;
  border-left:3px solid var(--accent);border-top:3px solid var(--accent);
  border-radius:2px 0 0 0;
}
/* The frame is decoration; on a narrow screen it just eats width. The wrap
   only has 16px of side padding until 900px, so the overhang has to stay
   inside that or it puts a couple of pixels of horizontal scroll on the page. */
@media (max-width:899.98px){
  .framed::before{inset:.7rem -.7rem -.7rem .7rem}
  .framed::after{width:18px;height:18px}
}

/* Caption plate. A small pinned label over the bottom of a framed photo. */
.frame-note{
  position:absolute;left:-.35rem;bottom:-.85rem;z-index:2;
  max-width:calc(100% - 2rem);
  padding:.5rem .9rem;
  background:var(--brand-deep);color:#e8f4fb;
  border-left:3px solid var(--accent);border-radius:2px;
  font-size:.86rem;line-height:1.35;
  box-shadow:0 10px 26px rgba(6,17,43,.34);
}
.frame-note strong{color:#fff;display:block;font-size:.94rem}

/* Section heading cluster, so every section head is built the same way. */
.sec-head{max-width:60rem}
.sec-head.center{margin-inline:auto;text-align:center}
.sec-head h2{margin-bottom:.35em}
.sec-head .lede{margin-bottom:0}

/* --- Long-form prose ----------------------------------------------------

   Book setting for the passages that are actually read rather than scanned:
   the opening paragraph carries a drop cap and sits flush, and every
   paragraph after it takes a two-space first-line indent. Applied only where
   there is a real run of prose - never to cards, ledes in isolation, form
   help, FAQ answers or anything that is interface rather than reading.
   --------------------------------------------------------------------- */
/* The tag is a label, not a paragraph, and the opening paragraph hangs flush
   under the drop cap, so both are excluded from the indent. */
.prose > p:not(.tag):not(.lede):not(.eyebrow){text-indent:2ch}

.prose--cap > p.lede::first-letter{
  float:left;
  font-family:"Barlow Condensed",Impact,sans-serif;
  font-weight:700;
  font-size:3.5em;
  line-height:.78;
  margin:.06em .1em 0 0;
  color:var(--accent);
}
/* A floated first letter and a hanging indent fight each other, and on a
   phone the cap eats a third of the measure. */
@media (max-width:559.98px){
  .prose--cap > p.lede::first-letter{font-size:2.9em}
  .prose > p:not(.tag):not(.lede):not(.eyebrow){text-indent:1.5ch}
}

/* --- Header -------------------------------------------------------------

   The navy utility bar and the white navigation bar are siblings, not nested:
   see templates/components/header.php. Only the white bar is sticky.

   Nothing between <body> and .site-header may set overflow, transform, filter,
   perspective or contain. Any of those would either clip the bar or become the
   containing block for it and kill position:sticky. That is why the page's
   horizontal overflow guard lives on <html> at the top of this file.
   --------------------------------------------------------------------- */

/* Utility bar: scrolls away with the page, on purpose. */
.util{background:var(--brand-deep);color:#c9e3f2;font-size:.9rem}
.util-inner{display:flex;align-items:center;gap:1.25rem;min-height:42px;flex-wrap:wrap}
.util p{margin:0}
.util-address{display:none;align-items:center;gap:.4rem}
.util-status{display:flex;align-items:center;gap:.45rem}
.util-phone{margin-left:auto;display:inline-flex;align-items:center;gap:.4rem;
  min-height:34px;padding:.25rem .35rem;border-radius:4px;
  color:#fff;font-weight:700;text-decoration:none}
.util-phone:hover{color:#ffc9e8}
.util .ic{width:15px;height:15px;fill:currentColor;flex:none}
.dot{width:9px;height:9px;border-radius:50%;background:#889eab;flex:none}
.dot.is-open{background:#3ddc8a;box-shadow:0 0 0 3px rgba(61,220,138,.22)}
.dot.is-closed{background:#ff8ace}
@media (min-width:760px){.util-address{display:inline-flex}}

/* The sticky bar. Sticky rather than fixed, so it keeps its place in the flow
   and nothing below it moves when it pins. z-index sits above every section on
   the page and below the popup (120) and the skip link (200). */
.site-header{
  position:sticky;
  top:0;
  z-index:100;
  background:#fff;
  box-shadow:0 1px 0 var(--line);
  transition:box-shadow .18s ease;
}
/* Stuck: a hairline plus a soft shadow, so the bar reads as a separate layer
   over the hero video, photographs and cards. The class is added by site.js;
   without scripting the bar is still sticky, it just keeps the plain hairline. */
.site-header.is-stuck{
  box-shadow:0 1px 0 var(--line),0 12px 26px -16px rgba(10,31,77,.5);
}

.nav-bar{
  display:flex;align-items:center;gap:.85rem;
  min-height:var(--nav-h);
  padding-block:.55rem;
  padding-left:env(safe-area-inset-left,0px);
  padding-right:env(safe-area-inset-right,0px);
  transition:min-height .18s ease,padding-block .18s ease;
}
/* Compact once scrolled. Deliberately only 12px: enough to feel tighter,
   not enough to make the logo hard to recognise. */
.site-header.is-stuck .nav-bar{min-height:var(--nav-h-stuck);padding-block:.3rem}

/* The bar stays in the flow, so shrinking it would shorten the document and
   step every following section upwards. The spacer takes back exactly what
   the bar gives up, measured by site.js, so the document height never changes
   and nothing below moves. It is only ever tall while the bar is compact, and
   it lives underneath the pinned bar where it cannot be seen. Without
   scripting .is-stuck is never set, the bar never compacts, and this stays at
   zero. */
.header-spacer{height:0}
.site-header.is-stuck + .header-spacer{height:var(--nav-shrink,0px)}

/* The gap is wider than it looks it needs to be. The shield artwork runs
   almost to the edge of its own box - only about 5px of the 54px is
   transparent - so a .7rem gap read as roughly 16px of daylight between the
   shield and the H of Ronnie Trammell, which is not enough separation for a mark and a
   wordmark sitting side by side. */
.brand{display:flex;align-items:center;gap:2rem;text-decoration:none;flex:none}
.brand-mark{width:54px;height:auto;transition:width .18s ease}
.site-header.is-stuck .brand-mark{width:46px}
.brand-words{display:flex;flex-direction:column;line-height:1}
.brand-name{font-family:"Barlow Condensed",sans-serif;font-weight:700;font-size:1.5rem;
  text-transform:uppercase;color:var(--brand-deep);letter-spacing:.01em}
.brand-sub{font-family:"Barlow Condensed",sans-serif;font-weight:600;font-size:1.02rem;
  text-transform:uppercase;letter-spacing:.34em;color:var(--accent)}

@media (prefers-reduced-motion:reduce) and (min-width:99999px){
  .site-header,.nav-bar,.brand-mark{transition:none}
}

.nav-desktop{display:none;margin-left:auto}
.nav-desktop > ul{display:flex;gap:.15rem;list-style:none;margin:0;padding:0}
.nav-desktop a{
  display:block;padding:.6rem .68rem;border-radius:4px;
  font-family:"Source Sans 3",sans-serif;font-size:.96rem;font-weight:600;
  letter-spacing:.01em;
  color:var(--brand-deep);text-decoration:none;white-space:nowrap;
}
.nav-desktop a:hover{background:var(--cream);color:var(--accent)}
.nav-desktop a[aria-current="page"]{color:var(--accent);box-shadow:inset 0 -3px 0 var(--accent)}

.nav-actions{margin-left:auto;display:flex;align-items:center;gap:.5rem;flex:none}
.nav-cta,.nav-portal{display:none}

/* Customer portal. Blue against the magenta call button: obviously a
   different action, still the two colors off the Ronnie Trammell shield. */
.btn-portal{background:var(--brand);border-color:var(--brand);color:#fff}
.btn-portal:hover{background:var(--brand-deep);border-color:var(--brand-deep);color:#fff}
.btn-portal .ic{width:16px;height:16px;fill:currentColor;flex:none}

/* Telephone on small screens, where "Call the store" will not fit. The utility
   bar carries the number as well, but that scrolls away. */
.nav-phone{
  width:48px;height:48px;display:grid;place-items:center;flex:none;
  background:var(--accent);border-radius:5px;color:#fff;text-decoration:none;
}
.nav-phone:hover{background:var(--accent-dark);color:#fff}
.nav-phone svg{width:22px;height:22px;fill:currentColor}

/* Services dropdown. The parent is a real link; the button and panel are an
   enhancement, so the menu works without JavaScript. */
.nav-desktop .has-menu{position:relative;display:flex;align-items:center}
.menu-toggle{
  display:grid;place-items:center;width:34px;height:44px;margin-left:-.4rem;
  background:none;border:0;color:var(--brand-deep);cursor:pointer;border-radius:4px;
}
.menu-toggle svg{width:18px;height:18px;fill:currentColor;transition:transform .16s ease}
.menu-toggle:hover{color:var(--accent)}
.menu-toggle[aria-expanded="true"] svg{transform:rotate(180deg)}
.submenu{
  display:block;position:absolute;top:100%;left:0;z-index:70;
  min-width:15rem;max-width:min(22rem,92vw);
  /* The top margin drops the panel from the bottom of the menu ITEM to the
     bottom edge of the bar, so its border never cuts across the white bar.
     Measured by site.js; the fallback is the un-compacted bar, which is the
     state you get with scripting off. */
  margin:var(--menu-drop) 0 0;padding:.4rem;list-style:none;
  background:#fff;border:1px solid var(--line);border-radius:6px;
  box-shadow:0 16px 40px rgba(10,31,77,.18);
}
.submenu[hidden]{display:none}
/* Flipped by script when opening leftward would run off the window edge. */
.submenu.is-flipped{left:auto;right:0}
.submenu li{margin:0}
.submenu a{
  display:block;padding:.55rem .75rem;border-radius:4px;white-space:nowrap;
  font-size:1.02rem;letter-spacing:.01em;
}
.submenu a:hover{background:var(--cream);color:var(--accent)}
.submenu-sep{height:1px;background:var(--line);margin:.4rem .55rem;padding:0}

/* Narrowing the window moves ordinary links out of the bar and into the
   Services dropdown, so the Call the store and Customer Portal buttons are
   never the things pushed off the end. Tiers are assigned in
   templates/components/header.php. */
.submenu > li.menu-tier-a,
.submenu > li.menu-tier-b{display:none}

.nav-toggle{
  width:48px;height:48px;display:grid;place-items:center;flex:none;
  background:var(--brand-deep);border:0;border-radius:5px;cursor:pointer;
}
.nav-toggle-bars{display:block;width:22px}
.nav-toggle-bars span{display:block;height:2.5px;background:#fff;border-radius:2px;margin:4.5px 0;
  transition:transform .2s ease,opacity .2s ease}
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(2){opacity:0}
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

/* The drawer hangs off the bottom of the sticky bar, so it opens below it and
   scrolls inside itself instead of running off the bottom of the window.
   site.js freezes the page behind it while it is open. */
.nav-mobile{
  background:#fff;border-top:1px solid var(--line);
  padding:.6rem env(safe-area-inset-right,0px)
          calc(1.4rem + env(safe-area-inset-bottom,0px))
          env(safe-area-inset-left,0px);
  max-height:calc(100vh - var(--nav-off));
  max-height:calc(100dvh - var(--nav-off));
  overflow-y:auto;
  overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
}
.nav-mobile ul{list-style:none;margin:0;padding:0}
.nav-mobile a{
  display:block;padding:.92rem 1rem;border-bottom:1px solid var(--line);
  font-family:"Source Sans 3",sans-serif;font-size:1.04rem;font-weight:600;
  color:var(--brand-deep);text-decoration:none;
}
.nav-mobile a:hover{background:var(--cream);color:var(--accent)}
.nav-mobile a[aria-current="page"]{color:var(--accent)}
.nav-mobile a.is-sub{padding-left:2.1rem;font-size:1.05rem;color:var(--muted);
  text-transform:none;font-weight:500}
.nav-mobile a.is-sub::before{content:"\2014\00a0";color:var(--accent)}
.nav-mobile-foot{padding:1.1rem 1rem 0}
.nav-mobile-foot .btn + .btn{margin-top:.6rem}
.nav-mobile-hours{margin:.7rem 0 0;text-align:center;color:var(--muted);font-size:.95rem}

/* Very small phones: the wordmark and the shield come down a little so the
   logo, the telephone button and the menu button all stay comfortably on one
   line. The shield stays large enough to still read as the logo. */
@media (max-width:419.98px){
  .brand-mark{width:44px}
  .site-header.is-stuck .brand-mark{width:40px}
  .brand-name{font-size:1.3rem}
  .brand-sub{font-size:.88rem;letter-spacing:.26em}
  .nav-bar{gap:.55rem}
}

/* Desktop navigation breakpoints.
   These come after every base nav rule on purpose: they share specificity
   with the defaults above, so order decides the winner. */
/* Phone and tablet: menu button on the left, the logo centred between the
   two equal-width controls, telephone on the right. The mark is the first
   thing you see on landing instead of sitting in a corner. */
@media (max-width:999.98px){
  .nav-toggle{order:1}
  .brand{order:2;margin-inline:auto;gap:1.1rem}
  .nav-actions{order:3;margin-left:0}
  .brand-mark{width:50px}
  .site-header.is-stuck .brand-mark{width:44px}
}
@media (max-width:419.98px){
  .brand-mark{width:46px}
  .site-header.is-stuck .brand-mark{width:41px}
}

@media (min-width:1000px){
  .nav-desktop{display:block}
  .nav-actions{margin-left:.5rem}
  .nav-cta,.nav-portal{display:inline-flex}
  .nav-phone{display:none}
  .nav-toggle{display:none}
  .nav-mobile{display:none}
}
/* Service Area steps out of the bar first. */
@media (max-width:1079.98px){
  .nav-desktop > ul > li.menu-tier-b{display:none}
  .submenu > li.menu-tier-b{display:block}
}
/* Then Home, Reviews and FAQs, along with anything added to the menu later. */
@media (max-width:1239.98px){
  .nav-desktop > ul > li.menu-tier-a{display:none}
  .submenu > li.menu-tier-a{display:block}
}

/* --- Hero ---------------------------------------------------------------

   The commercial plays as the background, on its own, the way it did on the
   old site. Everything in front of it sits on a scrim that is doing
   accessibility work rather than decoration: white text over moving footage
   fails contrast the instant a bright frame goes past, so the gradient
   guarantees the floor whatever the video happens to be showing.

   The video is decorative. It is aria-hidden, it carries no audio track at
   all, and the poster underneath is what a visitor sees if it never loads -
   so nothing on this section depends on it.
   --------------------------------------------------------------------- */
.hero{
  position:relative;isolation:isolate;overflow:hidden;
  background:linear-gradient(180deg,#0c4468 0%,var(--brand-deep) 62%,#082033 100%);
  color:#fff;
}

.hero-media{position:absolute;inset:0;z-index:-2;pointer-events:none;background:#082033}
.hero-media-still,
.hero-media-video{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:50% 45%;
}
/* The poster sits ON TOP of the video and fades out once playback actually
   starts. The other way round (video on top, revealed when playing) needs
   the video hidden until then - and a display:none video cannot play on
   iOS, so it would never start. Do not swap these. */
.hero-media-video{z-index:1}
.hero-media-still{z-index:2;transition:opacity .9s ease}
.hero-media.is-playing .hero-media-still{opacity:0}
/* A very slow drift, so a still frame never looks like a broken video and a
   playing one gains a little depth. Pure transform: no layout, no repaint. */
.js .hero-media-still,
.js .hero-media-video{animation:heroDrift 38s ease-in-out infinite alternate}
@keyframes heroDrift{
  from{transform:scale(1.06) translate3d(0,0,0)}
  to  {transform:scale(1.13) translate3d(-1.4%,-1.1%,0)}
}

/* The readability floor. Left-weighted on a wide screen where the copy is in
   a column; vertical on a phone where the copy runs the full width. */
.hero-scrim{
  position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:
    linear-gradient(180deg,rgba(6,17,40,.62) 0%,rgba(6,17,40,.30) 26%,rgba(6,17,40,.30) 62%,rgba(6,17,40,.80) 100%),
    linear-gradient(90deg,rgba(8,32,58,.93) 0 30%,rgba(8,32,58,.74) 52%,rgba(8,32,58,.30) 78%,rgba(8,32,58,.22) 100%);
}
/* One soft crease of brand light, so the field is not a flat wash. */
.hero-scrim::after{
  content:"";position:absolute;inset:0;
  background:
    radial-gradient(90% 70% at 8% 4%,rgba(51,144,194,.30) 0%,transparent 62%),
    linear-gradient(101deg,transparent 0 44%,rgba(255,255,255,.05) 44.3% 45.7%,transparent 46% 100%);
}

.hero-inner{
  position:relative;
  display:grid;gap:clamp(2rem,4vw,3.25rem);
  align-items:center;
  padding:clamp(3rem,6.4vw,5.6rem) 0 clamp(3.4rem,6vw,5.4rem);
  min-height:clamp(30rem,74vh,46rem);
}
/* Two columns from 960px: the copy, and the shield beside it. Restored
   2 September 2026 after a few hours without it - see the note on .hero-badge
   in templates/pages/home.php. Below 960 the grid stays single column and the
   shield is hidden, with .hero-mark carrying the brand instead. */
@media (min-width:960px){
  .hero-inner{grid-template-columns:minmax(0,1.02fr) minmax(0,.98fr)}
}

.hero-copy{max-width:36rem;min-width:0}
.hero h1{
  color:#fff;margin-bottom:.34em;
  font-size:clamp(2.5rem,5.1vw,4.05rem);
  line-height:1.02;
  text-shadow:0 2px 26px rgba(4,14,32,.5);
}
.hero h1 .accent{color:#ff5aba;display:block}
.hero-lede{
  font-size:clamp(1.06rem,1.55vw,1.24rem);
  color:#dfeff8;max-width:34rem;margin-bottom:1.5rem;
  text-shadow:0 1px 14px rgba(4,14,32,.55);
}

/* The brand introducing itself, above the headline. Phones only: the sticky
   bar already carries a small mark for navigation, and on a wide screen two
   logos stacked would just be two logos stacked. */
.hero-mark{display:none}
@media (max-width:959.98px){
  .hero-mark{
    display:block;width:clamp(74px,21vw,104px);margin:0 auto 1.15rem;
    filter:drop-shadow(0 10px 22px rgba(4,14,32,.55));
  }
  .hero-mark img{width:100%;height:auto;display:block}
}

/* Credentials as a stitched strip rather than three more bullet points. */
.hero-marks{
  list-style:none;margin:0 0 1.7rem;padding:1.05rem 0 0;
  border-top:2px dashed rgba(255,255,255,.24);
  display:flex;flex-wrap:wrap;gap:.55rem 1.6rem;
}
.hero-marks li{
  margin:0;display:flex;align-items:baseline;gap:.5rem;
  color:#e4f1fa;font-size:.97rem;
}
.hero-marks b{
  font-family:"Barlow Condensed",sans-serif;font-size:1.5rem;font-weight:700;
  color:#fff;line-height:1;letter-spacing:.01em;
}
.hero-marks span{color:#bce0f4}

/* Actions. The telephone number never breaks, and the label above it does the
   explaining so the number can stay one clean line. */
.hero-actions{display:flex;flex-wrap:wrap;gap:.8rem;align-items:stretch}
.btn-call{
  position:relative;overflow:hidden;
  display:inline-flex;align-items:center;gap:.7rem;
  padding:.7rem 1.35rem;min-height:60px;
  background:var(--accent);border:2px solid var(--accent);border-radius:5px;
  color:#fff;text-decoration:none;
  box-shadow:0 12px 26px rgba(166,32,110,.34);
  transition:background-color .15s ease,border-color .15s ease,transform .12s ease,box-shadow .15s ease;
}
.btn-call:hover{background:var(--accent-dark);border-color:var(--accent-dark);color:#fff;
  transform:translateY(-2px);box-shadow:0 18px 34px rgba(166,32,110,.42)}
.btn-call:active{transform:translateY(1px)}
.btn-call .ic{width:19px;height:19px;fill:currentColor;flex:none;opacity:.92}
.btn-call-lines{display:flex;flex-direction:column;line-height:1.12;text-align:left}
.btn-call-label{
  font-family:"Source Sans 3",sans-serif;
  font-size:.74rem;font-weight:700;letter-spacing:.15em;text-transform:uppercase;
  color:#ffd7ee;
}
.btn-call-num{
  font-family:"Barlow Condensed",sans-serif;
  font-size:1.6rem;font-weight:700;letter-spacing:.02em;
  white-space:nowrap;               /* the number never wraps */
}
.hero-actions .btn{min-height:60px}

/* Pause control.

   Asked for as "no controls", and visually that is what this is: it is
   transparent until the hero is hovered or the button takes keyboard focus.
   It is NOT display:none and it is NOT removed, because a decorative video
   that plays automatically for more than five seconds needs a way to stop it
   (WCAG 2.2.2). Hidden until wanted satisfies both. */
.hero-video-toggle{
  /* Bottom LEFT on purpose. The accessibility widget owns the bottom-right
     corner of every page, and two round buttons stacked in one corner is
     how you end up with a control nobody can press. */
  position:absolute;left:1rem;bottom:1rem;z-index:4;
  opacity:0;
  transition:opacity .22s ease,background-color .15s ease,transform .15s ease;
  display:grid;place-items:center;
  width:40px;height:40px;padding:0;border-radius:50%;cursor:pointer;
  background:rgba(6,17,43,.62);color:#fff;border:1px solid rgba(255,255,255,.34);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
}
.hero:hover .hero-video-toggle,
.hero-video-toggle:focus-visible{opacity:1}
.hero-video-toggle:hover{background:rgba(6,17,43,.94);transform:scale(1.06);opacity:1}
/* A touch screen has no hover, so there it stays reachable but quiet. */
@media (hover:none){.hero-video-toggle{opacity:.55}}
.hero-video-toggle [data-video-toggle-label]{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0;
}
.hero-video-toggle .ic{width:15px;height:15px;fill:currentColor}

/* Scroll cue: a line that runs downward, not a bouncing chevron. */
.hero-cue{
  position:absolute;left:50%;bottom:1.1rem;z-index:3;
  transform:translateX(-50%);
  width:26px;height:44px;border-radius:13px;
  border:1px solid rgba(255,255,255,.34);
  display:grid;place-items:start center;padding-top:8px;
}
.hero-cue-line{
  display:block;width:2px;height:9px;border-radius:2px;background:#fff;opacity:.85;
}
.js .hero-cue-line{animation:cueRun 2s ease-in-out infinite}
@keyframes cueRun{
  0%{transform:translateY(0);opacity:0}
  22%{opacity:.9}
  70%{transform:translateY(15px);opacity:0}
  100%{transform:translateY(15px);opacity:0}
}
@media (max-width:959.98px){.hero-cue{display:none}}

/* --- Hero on a phone ----------------------------------------------------
   Centred, because a left-aligned column inside a full-width dark panel
   reads as an accident on a 390px screen. Buttons go full width and the
   credential strip becomes rows so nothing is ever orphaned mid-line.
   --------------------------------------------------------------------- */
@media (max-width:959.98px){
  .hero-media-still,.hero-media-video{object-position:50% 40%}
  .hero-scrim{
    /* Lighter than it looks like it should be: the footage IS the point on
       a phone, and the copy is centred over the calmer middle of the frame.
       Checked for contrast against the brightest frame in the loop. */
    background:linear-gradient(180deg,
      rgba(6,24,44,.72) 0%,rgba(6,24,44,.44) 30%,
      rgba(6,24,44,.56) 66%,rgba(5,15,34,.90) 100%);
  }
  .hero-inner{
    padding:clamp(2rem,7vw,2.9rem) 0 clamp(2.2rem,7vw,3rem);
    min-height:0;
    justify-items:center;
  }
  .hero-copy{max-width:34rem;text-align:center;width:100%;position:relative}
  /* A soft pool of shade behind the words only.
     Measured, not guessed: with the frame scrim alone the lede fell to a
     2.94:1 contrast ratio over the bright roof in the footage, under the
     4.5:1 AA floor. Darkening the whole frame fixed it but made the hero
     flat and hid the property, which is the one thing worth showing. This
     puts the shade only where the text is and leaves the rest bright. */
  .hero-copy::before{
    content:"";position:absolute;z-index:-1;pointer-events:none;
    inset:-1.6rem -1.5rem -1.4rem;
    background:radial-gradient(118% 66% at 50% 48%,
      rgba(5,18,36,.86) 0%,rgba(5,18,36,.74) 46%,
      rgba(5,18,36,.42) 76%,rgba(5,18,36,0) 100%);
  }
  .hero-lede{margin-left:auto;margin-right:auto}
  .hero-marks{
    justify-content:center;
    gap:.5rem 1.25rem;
    padding-top:1rem;margin-bottom:1.5rem;
    border-bottom:2px dashed rgba(255,255,255,.24);padding-bottom:1rem;
  }
  .hero-actions{justify-content:center}
  /* Top corner on a phone: at the bottom it sat directly on the second
     call-to-action button. */
  .hero-video-toggle{left:.7rem;top:.7rem;bottom:auto;width:36px;height:36px}
}
@media (max-width:639.98px){
  /* One credential per row: three across always broke badly. */
  .hero-marks{flex-direction:column;align-items:center;gap:.5rem}
  .hero-marks li{gap:.45rem}
  .hero-marks b{font-size:1.3rem}
  .hero-actions{flex-direction:column;gap:.6rem;width:100%}
  .hero-actions .btn,.btn-call{width:100%;justify-content:center}
  .btn-call-lines{text-align:center}
  .hero h1{font-size:clamp(2.15rem,9.4vw,2.9rem)}
}
/* --- Trust strip --------------------------------------------------------
   Four facts, separated by stitching rather than by boxes. The open/closed
   state is NOT repeated here: it already sits in the utility bar. */
.trust{background:var(--brand);color:#fff;position:relative}
.trust-grid{
  display:grid;gap:0;
  grid-template-columns:repeat(2,1fr);
  padding:.35rem 0;
}
@media (min-width:860px){.trust-grid{grid-template-columns:repeat(4,1fr)}}
.trust-item{
  padding:1.05rem 1.1rem;text-align:center;position:relative;
}
.trust-item + .trust-item::before{
  content:"";position:absolute;left:0;top:22%;bottom:22%;
  border-left:2px dashed rgba(255,255,255,.2);
}
@media (max-width:859.98px){
  .trust-item:nth-child(odd)::before{display:none}
}
.trust-item strong{
  display:block;
  font-family:"Source Sans 3",sans-serif;
  font-size:1.03rem;font-weight:700;letter-spacing:.01em;color:#fff;
}
.trust-item span{display:block;color:#b9d8ea;font-size:.9rem;margin-top:.2rem}

/* --- Generic grid (services page, city pages, contact) ------------------ */
.grid{display:grid;gap:1.5rem}
.grid-2,.grid-3{grid-template-columns:1fr}
@media (min-width:680px){.grid-2,.grid-3{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:1020px){.grid-3{grid-template-columns:repeat(3,minmax(0,1fr))}}

/* Cards on those pages keep the same skin as the home page services. */
.card{
  display:flex;flex-direction:column;position:relative;
  background:var(--paper);border:1px solid var(--line);border-radius:6px;overflow:hidden;
  transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease;
}
.card:hover{transform:translateY(-3px);box-shadow:0 16px 36px rgba(10,31,77,.13);border-color:#bcd7e6}
.card-media{aspect-ratio:16/10;background:var(--cream);overflow:hidden}
.card-media img{width:100%;height:100%;object-fit:cover}
.card-body{padding:1.25rem 1.3rem 1.4rem;display:flex;flex-direction:column;flex:1}
.card h3{margin-bottom:.4rem}
.card h3 a{color:inherit;text-decoration:none}
.card h3 a:hover{color:var(--accent)}
.card p{color:var(--muted);margin-bottom:1rem}
.card-link{
  margin-top:auto;display:inline-flex;align-items:center;gap:.42rem;
  font-family:"Source Sans 3",sans-serif;font-size:.92rem;font-weight:700;
  color:var(--brand);text-decoration:none;
}
.card-link:hover{color:var(--accent)}
.card-link svg{width:14px;height:14px;fill:currentColor;transition:transform .16s ease}
.card-link:hover svg{transform:translateX(3px)}

/* --- Service cards ------------------------------------------------------
   One lead service on a wide band, then two mid cards and three compact
   ones. Same components throughout, three different weights, so the section
   has a shape instead of being nine identical rectangles. */
.svc-grid{display:grid;gap:1.15rem;grid-template-columns:repeat(6,1fr)}
.svc{
  grid-column:1/-1;
  display:flex;flex-direction:column;
  background:var(--paper);border:1px solid var(--line);border-radius:6px;
  overflow:hidden;position:relative;
  transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease;
}
.svc:hover{transform:translateY(-3px);box-shadow:0 16px 36px rgba(10,31,77,.13);border-color:#bcd7e6}
/* A thread of brand colour down the opening edge of every card. */
.svc::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:3px;z-index:2;
  background:linear-gradient(180deg,var(--accent) 0 34%,var(--brand-soft) 34% 100%);
  opacity:0;transition:opacity .16s ease;
}
.svc:hover::before,.svc:focus-within::before{opacity:1}
.svc-media{background:var(--cream);overflow:hidden;aspect-ratio:16/10}
.svc-media img{width:100%;height:100%;object-fit:cover;transition:transform .4s ease}
.svc:hover .svc-media img{transform:scale(1.035)}
.svc-body{padding:1.1rem 1.25rem 1.25rem;display:flex;flex-direction:column;flex:1}
.svc h3{margin-bottom:.35rem;font-size:1.28rem}
.svc h3 a{color:inherit;text-decoration:none}
.svc h3 a::after{content:"";position:absolute;inset:0}   /* whole card is the target */
.svc:hover h3 a{color:var(--accent)}
.svc p{color:var(--muted);margin-bottom:1rem;font-size:1rem}
.svc-link{
  margin-top:auto;display:inline-flex;align-items:center;gap:.42rem;
  font-family:"Source Sans 3",sans-serif;
  font-size:.92rem;font-weight:700;letter-spacing:.02em;
  color:var(--brand);text-decoration:none;
}
.svc:hover .svc-link{color:var(--accent)}
.svc-link svg{width:14px;height:14px;fill:currentColor;transition:transform .16s ease}
.svc:hover .svc-link svg{transform:translateX(3px)}

@media (min-width:660px){
  .svc--wide{grid-column:span 3}
  .svc--narrow{grid-column:span 3}
  .svc--wide .svc-media{aspect-ratio:16/10}
}
@media (min-width:1000px){
  .svc--narrow{grid-column:span 2}
  .svc--narrow .svc-media{aspect-ratio:16/9}
  .svc--wide .svc-media{aspect-ratio:21/9}
  /* The lead service reads across the full width, picture beside the copy. */
  .svc--feature{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);align-items:stretch}
  .svc--feature .svc-media{aspect-ratio:auto;height:100%;min-height:17rem}
  .svc--feature .svc-body{padding:1.7rem 2rem;justify-content:center}
  .svc--feature h3{font-size:1.85rem}
  .svc--feature p{font-size:1.08rem;max-width:44ch;margin-bottom:.35rem}
  .svc--feature .svc-link{margin-top:.9rem}
}
/* --- The last row, when the count is not a round number -------------------
   The grid lays the compact cards three across on a desktop and two across on
   a tablet, which closes square only when the number of services happens to
   divide. It stopped dividing the moment services became seasonal: with
   Christmas lights resting the row ends two cards short, and from February to
   August, when the leaf work rests as well, it ends one card short. That left
   a quarter of the last row empty and looked like something had failed to
   load.

   So the leftovers stretch to finish the row. Two left over take half each.
   One left over runs the full width with its picture beside the copy, the
   same shape as the lead card, which closes the grid off deliberately instead
   of leaving a hole. The selectors are positional, so this keeps working
   whatever the count turns out to be. */
@media (min-width:1000px){
  /* Two on the final row: half the width each. */
  .svc--narrow:last-child:nth-child(3n+2),
  .svc--narrow:nth-last-child(2):nth-child(3n+1){grid-column:span 3}
  .svc--narrow:last-child:nth-child(3n+2) .svc-media,
  .svc--narrow:nth-last-child(2):nth-child(3n+1) .svc-media{aspect-ratio:21/9}

  /* One on the final row: run it across, picture beside the copy. */
  .svc--narrow:last-child:nth-child(3n+1){
    grid-column:span 6;
    display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);
    align-items:stretch;
  }
  .svc--narrow:last-child:nth-child(3n+1) .svc-media{
    aspect-ratio:auto;height:100%;min-height:14rem;
  }
  .svc--narrow:last-child:nth-child(3n+1) .svc-body{
    padding:1.5rem 1.9rem;justify-content:center;
  }
}

/* Tablet lays the compact cards two across, so the odd one out is the one
   that lands on an even position. This case predates the seasonal work - with
   twelve services the tablet grid has always ended a card short. */
@media (min-width:660px) and (max-width:999.98px){
  .svc--narrow:last-child:nth-child(2n){grid-column:span 6}
  .svc--narrow:last-child:nth-child(2n) .svc-media{aspect-ratio:21/9}
}

/* Lead card carries a little more weight than the rest. */
.svc--feature{border-color:#c8dfea;box-shadow:0 10px 30px rgba(10,31,77,.08)}
.svc--feature::before{opacity:1;width:4px}

/* --- Split feature ------------------------------------------------------ */
.split{display:grid;gap:clamp(2rem,4vw,3.4rem);align-items:center}
@media (min-width:900px){
  .split{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .split--reverse .split-media{order:2}
}
.split-media img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block}
.checklist{list-style:none;padding:0;margin:1.2rem 0 0;display:grid;gap:.6rem}
.checklist li{display:flex;gap:.65rem;align-items:flex-start}
.checklist svg{width:20px;height:20px;fill:var(--accent);flex:none;margin-top:.24rem}

/* A second, smaller photograph tucked over the corner of the first. */
.stack-shot{position:relative}
.stack-shot .stack-inset{
  position:absolute;right:-1.1rem;bottom:-1.6rem;z-index:3;
  width:42%;max-width:14rem;
  border:4px solid #fff;border-radius:4px;overflow:hidden;
  box-shadow:0 16px 36px rgba(6,17,43,.3);
}
.stack-shot .stack-inset img{display:block;width:100%;aspect-ratio:1/1;object-fit:cover}
@media (max-width:899.98px){
  .stack-shot .stack-inset{width:38%;right:-.4rem;bottom:-1.1rem;border-width:3px}
}
/* Below this the inset and the caption plate were both anchored to the
   bottom of the same frame, so the photo landed on the caption and cut the
   words in half. The inset moves to the top corner and the caption keeps
   the full width it needs. */
@media (max-width:619.98px){
  .stack-shot .stack-inset{width:34%;right:-.4rem;top:-1rem;bottom:auto}
  .stack-shot .frame-note{max-width:calc(100% - .7rem)}
}

/* --- Method panel (why us / commitment) --------------------------------- */
.method{
  background:
    radial-gradient(90% 120% at 88% 8%,#1b6d9f 0%,transparent 58%),
    linear-gradient(170deg,var(--brand-deep),#082033);
  color:#e8f4fb;position:relative;isolation:isolate;overflow:hidden;
}
.method h2{color:#fff}
.method .lede{color:#c6e1f0}
.method-grid{display:grid;gap:clamp(1.8rem,3.5vw,3rem);align-items:center}
@media (min-width:900px){.method-grid{grid-template-columns:minmax(0,1fr) minmax(0,.85fr)}}
/* Three plain facts on stitched plates instead of a wall of prose. */
.method-points{list-style:none;margin:1.5rem 0 0;padding:0;display:grid;gap:.7rem}
.method-points li{
  display:flex;gap:.8rem;align-items:flex-start;
  padding:.85rem 1rem;border-radius:4px;
  background:rgba(255,255,255,.045);
  border:1px solid rgba(255,255,255,.11);
  border-left:3px solid var(--accent);
}
.method-points svg{width:18px;height:18px;fill:#ff8ace;flex:none;margin-top:.25rem}
.method-points span{color:#dbeef8;font-size:1rem}

/* --- Price cards --------------------------------------------------------
   The figure is the thing being read, so it is set large and the currency
   mark and the unit are demoted around it rather than competing with it.
   .price-card--wide carries two figures (mulch and straw), so it spans two
   columns where there is room and stacks like the others where there is not.
   ---------------------------------------------------------------------- */
.price-grid{
  display:grid;gap:1.25rem;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr));
}
.price-card{
  min-width:0;
  background:var(--paper);border:1px solid var(--line);border-radius:6px;
  padding:1.5rem 1.4rem;
  display:flex;flex-direction:column;
  transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease;
}
.price-card:hover{transform:translateY(-3px);box-shadow:0 16px 36px rgba(10,31,77,.13);border-color:#bcd7e6}
.price-card--wide{grid-column:span 2}
@media (max-width:660px){.price-card--wide{grid-column:auto}}
.price-card h3{font-size:1.12rem;line-height:1.25;margin:0 0 .35rem;color:var(--brand-deep)}
.price-card .price-sub{margin-top:1.1rem}
.price-figure{
  display:flex;align-items:baseline;gap:.18rem;flex-wrap:wrap;
  margin:0 0 .55rem;color:var(--ink);
}
.price-cur{font-size:1.35rem;font-weight:700;color:var(--accent);line-height:1}
.price-num{font-size:clamp(2.4rem,5.4vw,3.1rem);font-weight:800;letter-spacing:-.02em;line-height:1}
.price-unit{font-size:.86rem;font-weight:600;color:var(--muted);margin-left:.35rem}
.price-note{color:var(--muted);font-size:.95rem;margin:0 0 1rem;flex:1}
.price-card .card-link{margin-top:auto}
/* --- Gallery ------------------------------------------------------------ */
.gallery{display:grid;gap:1rem;grid-template-columns:repeat(2,1fr)}
.gallery figure{margin:0;border-radius:5px;overflow:hidden;background:var(--cream);position:relative}
.gallery img{width:100%;aspect-ratio:4/3;object-fit:cover;transition:transform .4s ease;display:block}
.gallery figure:hover img{transform:scale(1.045)}
/* Four across on a desktop. As a 2x2 block this ran a screen and a half. */
@media (min-width:900px){.gallery{grid-template-columns:repeat(4,minmax(0,1fr))}}
.gallery figcaption{
  position:absolute;left:0;right:0;bottom:0;
  padding:1.6rem .85rem .7rem;
  background:linear-gradient(180deg,transparent,rgba(6,17,43,.86));
  color:#fff;font-size:.9rem;
}

/* The gallery page has room to breathe: three across rather than four, and
   a taller crop, because here the photographs are the content rather than a
   strip inside a longer page. */
.gallery--page{gap:1.25rem}
@media (min-width:680px){.gallery--page{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:1020px){.gallery--page{grid-template-columns:repeat(3,minmax(0,1fr))}}
.gallery--page img{aspect-ratio:3/2}

/* --- FAQ ---------------------------------------------------------------- */
.faq-list{max-width:48rem;margin-inline:auto;border-top:2px dashed rgba(18,48,110,.2)}
.faq-item{border-bottom:2px dashed rgba(18,48,110,.2)}
.faq-item summary{
  position:relative;list-style:none;cursor:pointer;
  padding:1.05rem 3rem 1.05rem 1.1rem;
  font-family:"Source Sans 3",sans-serif;
  font-size:1.08rem;font-weight:700;line-height:1.4;
  color:var(--brand-deep);
  border-left:3px solid transparent;
  transition:background-color .14s ease,border-color .14s ease,color .14s ease;
}
.faq-item summary::-webkit-details-marker{display:none}
.faq-item summary:hover{background:rgba(18,48,110,.045);border-left-color:var(--accent);color:var(--accent)}
.faq-item summary:focus-visible{outline:3px solid var(--accent);outline-offset:-3px;border-radius:3px}
.faq-item[open] summary{border-left-color:var(--accent);color:var(--brand)}
.faq-item summary::after{
  content:"";position:absolute;right:1.15rem;top:50%;
  width:9px;height:9px;
  border-right:2.5px solid var(--accent);border-bottom:2.5px solid var(--accent);
  transform:translateY(-65%) rotate(45deg);
  transition:transform .18s ease;
}
.faq-item[open] summary::after{transform:translateY(-25%) rotate(-135deg)}
.faq-answer{
  padding:.1rem 1.5rem 1.35rem 1.15rem;
  color:var(--muted);max-width:64ch;
  border-left:3px solid var(--accent);
}
.faq-answer p{margin-bottom:.6rem}
.faq-answer p:last-child{margin-bottom:0}

/* --- Service area ------------------------------------------------------- */
/* Nine towns, three by three. No stranded card on a final row, and an arrow
   rather than a map pin: a pin per town would read as a branch per town. */
.area-list{
  list-style:none;padding:0;margin:1.6rem auto 0;
  display:grid;gap:.7rem;
  grid-template-columns:repeat(2,minmax(0,1fr));
  max-width:56rem;
}
@media (min-width:720px){.area-list{grid-template-columns:repeat(3,minmax(0,1fr))}}
.area-list li{margin:0}
.area-list a{
  display:flex;align-items:center;gap:.6rem;
  min-height:56px;padding:.7rem 1rem;
  background:var(--paper);border:1px solid var(--line);border-radius:5px;
  color:var(--brand-deep);text-decoration:none;font-weight:600;
  transition:border-color .15s ease,color .15s ease,transform .15s ease,box-shadow .15s ease;
}
.area-list a:hover{
  border-color:var(--accent);color:var(--accent);
  transform:translateY(-2px);box-shadow:0 10px 22px rgba(10,31,77,.1);
}
.area-list svg{width:15px;height:15px;fill:var(--accent);flex:none;transition:transform .15s ease}
.area-list a:hover svg{transform:translateX(3px)}
.area-list .area-serving{
  font-size:.72rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted);display:block;line-height:1;margin-bottom:.2rem;
}
.area-list a:hover .area-serving{color:var(--accent)}
.area-note{
  margin:1.4rem auto 0;max-width:46rem;text-align:center;
  font-size:.95rem;color:var(--muted);
}

/* --- Review invitation -------------------------------------------------- */
/* Shown while no reviews are published. Deliberately small: an ask, not an
   empty testimonial section pretending to be content. */
.review-ask{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:1rem 1.6rem;max-width:60rem;margin:0 auto;
  padding:1.15rem 1.4rem;
  background:var(--paper);border:1px solid var(--line);
  border-left:3px solid var(--accent);border-radius:5px;
}
.review-ask p{margin:0;color:var(--muted)}
.review-ask strong{display:block;color:var(--brand-deep);font-size:1.06rem}
.review-ask .btn{flex:none}



/* --- Notice ------------------------------------------------------------- */
.notice{
  border-left:4px solid var(--brand);background:var(--cream);
  padding:1.15rem 1.35rem;border-radius:0 6px 6px 0;margin:1.75rem 0;
}
.notice p:last-child{margin-bottom:0}
.notice--accent{border-left-color:var(--accent)}

/* --- CTA band -----------------------------------------------------------
   Not a centred red rectangle with two buttons under it: the ask sits on the
   left and the two actions sit beside it, over a single pressed crease. */
.cta-band{
  background:
    radial-gradient(80% 130% at 82% 10%,#d83292 0%,transparent 62%),
    linear-gradient(160deg,var(--accent) 0%,var(--accent-dark) 100%);
  color:#fff;position:relative;isolation:isolate;overflow:hidden;
  border-top:2px dashed rgba(255,255,255,.28);
}
.cta-band h2{color:#fff;margin-bottom:.3em}
.cta-band p{color:#ffe2f3;max-width:52ch;margin-bottom:0}
.cta-inner{display:grid;gap:1.5rem;align-items:center}
@media (min-width:820px){
  .cta-inner{grid-template-columns:minmax(0,1fr) auto;gap:2.75rem}
}
.cta-actions{display:flex;flex-wrap:wrap;gap:.8rem}
.cta-band .btn-primary{background:#fff;border-color:#fff;color:var(--accent-dark)}
.cta-band .btn-primary:hover{background:var(--brand-deep);border-color:var(--brand-deep);color:#fff}

/* --- Footer -------------------------------------------------------------
   Four columns on a desktop: who this is, popular services, company, contact.
   Then a separate, quieter bottom bar for the copyright, the two legal pages
   and the build credit.

   Two rules govern the whole thing. It stays SHORT - this is a local service
   company, not a marketplace, and a footer that runs a screen is a footer
   nobody reads. And the address and telephone number are never collapsed at
   any width, because they are the reason most people scroll down here.
   ---------------------------------------------------------------------- */
.site-footer{background:var(--brand-deep);color:#cfe6f4;margin-top:0}

.footer-grid{
  display:grid;
  gap:1.5rem 1.6rem;
  padding:clamp(2rem,3.6vw,2.75rem) 0 1.5rem;
}

/* Phone: one column, every link group a full-width control. */
@media (max-width:599.98px){
  .footer-grid{grid-template-columns:minmax(0,1fr);gap:.75rem;padding:1.5rem 0 1rem}
  .footer-contact{order:5}
}

/* Small tablet: a two by two block. Identity beside the services list,
   company beside contact. Deliberately NOT identity across the top with the
   three others stacked underneath - that arrangement pushed the contact
   column onto a row of its own and made the footer nearly a full screen. */
@media (min-width:600px) and (max-width:767.98px){
  .footer-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:1.6rem 2rem}
}

/* Tablet and small laptop: identity across the top, then the three working
   columns in one row. Putting the tallest two columns side by side rather
   than one after the other is what keeps this to a reasonable height. */
@media (min-width:768px) and (max-width:959.98px){
  .footer-grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:1.4rem 2rem;padding-top:2rem}
  .footer-brand{grid-column:1/-1;display:flex;flex-wrap:wrap;align-items:center;gap:.5rem 1.5rem}
  .footer-brand .footer-tag{margin:0;flex:1 1 18rem}
  .footer-brand .footer-facts{margin:0}
  .footer-brand .footer-portal{margin:0;flex-basis:100%}
}

/* Desktop: four columns with real breathing room between them. The identity
   column carries the mark and the description so it takes a little more; the
   two link columns are the narrowest things here. The gap scales with the
   viewport, so 1024 stays four columns without the words colliding. */
@media (min-width:960px){
  .footer-grid{
    grid-template-columns:1.24fr .92fr .84fr 1.06fr;
    gap:1.1rem clamp(1.6rem,3.2vw,3.6rem);
    padding:clamp(2.2rem,3vw,2.9rem) 0 1.7rem;
  }
}

.footer-col{min-width:0}

/* --- 1. Identity --------------------------------------------------------- */
.footer-ident{display:flex;align-items:center;gap:.8rem}
.footer-logo{
  width:52px;height:auto;flex:none;
  background:#fff;padding:5px;border-radius:6px;
}
.footer-name{
  margin:0;color:#fff;font-weight:700;line-height:1.25;
  font-size:1.02rem;letter-spacing:.01em;
}
.footer-tag{margin:.75rem 0 0;color:#a9ccdf;font-size:.93rem;line-height:1.5;max-width:34ch}
.footer-facts{
  list-style:none;margin:.85rem 0 0;padding:0;
  display:flex;flex-wrap:wrap;gap:.4rem .5rem;
}
.footer-facts li{
  font-size:.8rem;font-weight:600;letter-spacing:.02em;
  color:#dfeff8;background:rgba(255,255,255,.09);
  border:1px solid rgba(255,255,255,.16);
  border-radius:999px;padding:.24rem .6rem;
}

/* Secondary route to the customer portal. The button in the navigation bar is
   the primary one; this is for people already at the bottom of a long page. */
.footer-portal{margin:.9rem 0 0}
.footer-portal a{display:inline-flex;align-items:center;gap:.45rem;
  min-height:44px;padding:.3rem .8rem .3rem 0;
  color:#fff;font-weight:700;font-size:.95rem;
  text-decoration:underline;text-underline-offset:.2em}
.footer-portal .ic{width:16px;height:16px;fill:currentColor;flex:none}

/* --- Headings ------------------------------------------------------------
   Interface type, not poster type. */
.footer-head{
  font-family:"Source Sans 3",sans-serif;
  font-size:.76rem;font-weight:700;letter-spacing:.15em;text-transform:uppercase;
  color:#a8d0e5;margin:0 0 .8rem;
}
p.footer-head{margin-bottom:.7rem}

/* --- 2 and 3. Link columns ---------------------------------------------- */
.footer-col ul{list-style:none;margin:0;padding:0}
.footer-links li{margin-bottom:.3rem}
.footer-col a{color:#d8ecf7;text-decoration:none;font-size:.94rem;line-height:1.45}
.footer-col a:hover{color:#fff;text-decoration:underline;text-underline-offset:.18em}
.footer-col a:focus-visible,
.footer-base a:focus-visible,
.footer-group > summary:focus-visible{
  outline:3px solid #ff9ad5;outline-offset:3px;border-radius:3px;
  color:#fff;
}

/* The route to the other six services reads as a route, not as a seventh
   service. */
.footer-more{margin-top:.55rem}
.footer-more a{
  color:#9fcbe0;font-size:.86rem;font-weight:600;
  text-decoration:underline;text-underline-offset:.2em;
}
.footer-more a:hover{color:#ff9ad5}

/* --- 4. Contact ---------------------------------------------------------- */
.footer-address{
  font-style:normal;font-size:.95rem;line-height:1.55;
  display:grid;gap:.1rem;
}
.footer-street,.footer-town{color:#dfeff8}
.footer-phone{
  display:inline-flex;align-items:center;gap:.45rem;
  margin-top:.45rem;min-height:38px;
  color:#fff;font-weight:700;font-size:1.06rem;letter-spacing:.01em;
  text-decoration:none;
}
.footer-phone .ic{width:17px;height:17px;fill:#ff9ad5;flex:none}
.footer-phone:hover{color:#ff9ad5;text-decoration:underline;text-underline-offset:.2em}
.footer-mail{color:#fff;font-weight:600}

.footer-actions{margin:.85rem 0 0 !important;display:grid;gap:.35rem}
.footer-actions a{
  display:inline-flex;align-items:center;gap:.5rem;
  min-height:34px;font-weight:600;
}
.footer-social .ic{width:17px;height:17px;fill:currentColor;flex:none;opacity:.85}
.footer-social:hover .ic{opacity:1}

/* --- Footer groups ------------------------------------------------------
   Real <details>, so expanded and collapsed states are announced and the
   keyboard works without any help from us. They arrive open; site.js closes
   them below 600px and pins them open above it. */
.footer-group{border:0}
.footer-group > summary{
  list-style:none;cursor:pointer;
  display:flex;align-items:center;gap:.6rem;
}
.footer-group > summary::-webkit-details-marker{display:none}
.footer-head-short{display:none}
.footer-group--hours{margin-top:.9rem}
.footer-head--sub{margin-bottom:.5rem}

/* Desktop: the summary is the column heading and nothing more. site.js also
   blocks the toggle up here, so it cannot be collapsed by mouse or key. */
@media (min-width:600px){
  .footer-group > summary{cursor:default}

  /* The safety net. A desktop visitor must never meet a collapsed footer
     column, so the content is forced visible in CSS rather than trusting the
     open attribute to have been put back by script. Browsers that do not know
     ::details-content ignore this rule and fall back to site.js, and the
     groups ship open in the HTML either way. */
  .footer-group::details-content{
    content-visibility:visible;
    display:block;
    opacity:1;
    block-size:auto;
  }
  /* Same intent for engines that expose the slot instead.
     Scoped to the CLOSED state on purpose. Unscoped, this selector is more
     specific than a plain class rule and quietly reverted the display of
     anything inside a group that the stylesheet had laid out itself - the
     grouped hours list lost its two-column grid to it. It only ever needed to
     apply when the details is closed, which on a desktop it never is. */
  .footer-group:not([open]) > summary ~ *{display:revert}
}

/* Phone: the summary becomes a real control with a chevron and a tap target
   that clears 44px. */
@media (max-width:599.98px){
  .footer-brand{padding-bottom:.35rem}
  .footer-head-full{display:none}
  .footer-head-short{display:inline}
  .footer-group > summary{
    min-height:46px;padding:.35rem 0;margin:0;
    border-top:1px solid rgba(255,255,255,.14);
    color:#dfeff8;font-size:.8rem;
  }
  .footer-group > summary::after{
    content:"";margin-left:auto;
    width:9px;height:9px;
    border-right:2px solid #a8d0e5;border-bottom:2px solid #a8d0e5;
    transform:translateY(-25%) rotate(45deg);
    transition:transform .18s ease;
  }
  .footer-group[open] > summary::after{transform:translateY(10%) rotate(-135deg)}
  .footer-group[open] > summary{color:#fff}
  .footer-group > ul{padding:.15rem 0 .7rem}
  .footer-group .hours-compact{padding:.15rem 0 .7rem}
  .footer-links li{margin-bottom:0}
  .footer-links a{min-height:40px;display:flex;align-items:center}
  .footer-contact > p.footer-head{
    border-top:1px solid rgba(255,255,255,.14);
    padding-top:.9rem;margin-bottom:.5rem;
  }
  .footer-group--hours{margin-top:.35rem}

  /* The bottom bar stacks into three rows on a phone. Kept tight so it costs
     about one line of the footer's height rather than three. */
  .footer-base{font-size:.8rem}
  .footer-base-inner{padding:.6rem 0;gap:.12rem .9rem;line-height:1.35}
  .footer-copy,.footer-credit{flex:1 1 100%}
  .footer-actions{gap:.2rem}
  .footer-contact > p.footer-head{padding-top:.8rem}
}

/* Today's line. Phone only - on a desktop the full week is visible right
   underneath, so a today line there would just repeat it. */
.footer-today{
  display:none;
  margin:.8rem 0 0;flex-wrap:wrap;gap:.15rem .7rem;
  font-size:.92rem;color:#cfe6f4;
}
@media (max-width:599.98px){.footer-today{display:flex;margin:.5rem 0 0}}
.footer-today-status{display:inline-flex;align-items:center;gap:.4rem;font-weight:700;color:#fff}
.footer-today-hours strong{color:#fff}

/* Grouped hours: three lines rather than seven, and the day and the times sit
   on the SAME line. Stacked, three runs of hours cost six lines and made the
   contact column the tallest thing in the footer on its own. */
.footer-col .hours-compact{
  margin:0;font-size:.9rem;line-height:1.45;
  display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:.12rem .8rem;align-items:baseline;
}
.hours-compact dt{color:#a8d0e5;margin:0;white-space:nowrap}
/* The times are allowed to wrap rather than forced onto one line. Forced,
   they were wider than the column at 1024 and 768 and put the whole document
   into a horizontal scroll. They fit on one line at every tested width now;
   this is the guarantee that a longer opening time can never do it again. */
.hours-compact dd{margin:0;color:#dfeff8;font-weight:600;min-width:0}
@media (max-width:359.98px){
  /* Narrowest phones: let the times wrap under the day rather than push the
     column wider than the screen. */
  .footer-col .hours-compact{grid-template-columns:minmax(0,1fr);gap:0}
  .hours-compact dt{margin-top:.4rem}
  .hours-compact dt:first-child{margin-top:0}
}

.hours-table{width:100%;border-collapse:collapse;font-size:.92rem}
.hours-table th,.hours-table td{padding:.16rem 0;text-align:left;font-weight:400;color:#cfe6f4}
/* A day's hours belong on one line; wrapped, the column doubles in height. */
.hours-table td{white-space:nowrap}
.hours-table th{color:#a8d0e5;padding-right:.9rem;white-space:nowrap;font-weight:400}

/* --- Bottom bar ---------------------------------------------------------
   Deliberately quieter than the columns above it: darker ground, smaller
   type, and no headings. */
.footer-base{background:#061620;font-size:.86rem;border-top:1px solid rgba(255,255,255,.07)}
.footer-base-inner{
  display:flex;flex-wrap:wrap;gap:.45rem 1.6rem;
  align-items:center;justify-content:space-between;padding:.85rem 0;
}
.footer-base p{margin:0;color:#94b9cf}
.footer-base a{color:#bcdcee;text-decoration:none}
.footer-base a:hover{color:#fff;text-decoration:underline;text-underline-offset:.18em}
.footer-legal{
  list-style:none;margin:0;padding:0;
  display:flex;flex-wrap:wrap;align-items:center;gap:.3rem 1.1rem;
}
.footer-legal li{position:relative}
.footer-legal li + li::before{
  content:"";position:absolute;left:-.58rem;top:.32em;
  width:1px;height:.85em;background:rgba(255,255,255,.22);
}
/* Below a full desktop the bottom bar cannot hold copyright, two legal links
   and the credit on one line, and left to itself it wrapped into three. The
   legal links take their own row so it settles at two. */
@media (max-width:899.98px){
  .footer-base-inner{justify-content:space-between;gap:.35rem 1.2rem}
  .footer-legal{order:3;width:100%;gap:.3rem 1.1rem}
}
@media (max-width:699.98px){
  .footer-base-inner{justify-content:flex-start}
  .footer-legal li + li::before{display:none}
}

@media (prefers-reduced-motion:reduce) and (min-width:99999px){
  .footer-group > summary::after{transition:none}
}

/* --- FAQ hub group links ------------------------------------------------- */
.faq-group-link{margin:.35rem 0 0;font-size:.95rem}
.faq-group-link a{
  color:var(--brand);font-weight:600;
  text-decoration:underline;text-underline-offset:.2em;
}
.faq-group-link a:hover{color:var(--accent-dark)}

/* --- Review location picker ----------------------------------------------
   Ronnie Trammell has a Google listing per town and a review only counts on the listing it
   was left on, so the visitor is asked which one before being sent anywhere.
   Ships as a list of real links and is upgraded to a select by site.js. */
.review-picker{margin:0}
.review-picker-lead{margin:0 0 .2rem;font-weight:700;color:var(--brand-deep)}
.review-picker-hint{margin:0 0 .8rem;color:var(--muted);font-size:.93rem;max-width:54ch}
.review-picker-list{
  list-style:none;margin:0;padding:0;
  display:flex;flex-wrap:wrap;gap:.5rem;
}
.review-picker-list li{margin:0}
.review-picker-select{display:flex;flex-wrap:wrap;align-items:center;gap:.6rem}
.review-picker-select select{
  min-height:44px;padding:.5rem .7rem;
  border:1px solid var(--line);border-radius:6px;background:var(--paper);
  color:var(--ink);font:inherit;font-size:.98rem;min-width:13rem;
}
.review-picker-select select:focus-visible{
  outline:3px solid var(--accent);outline-offset:2px;
}
.review-picker-select .ve-lbl{
  margin:0;font-size:.86rem;font-weight:700;color:var(--brand-deep);
  width:100%;
}

/* On the dark review hero the label and the helper text have to flip. */
.review-picker--onDark .review-picker-lead,
.review-picker--onDark .review-picker-select .ve-lbl{color:#fff}
.review-picker--onDark .review-picker-hint{color:#cfe6f3}
.review-picker--onDark{margin-top:1.4rem}

.review-ask--picker{display:block}

/* --- Page hero (interior pages) ---------------------------------------- */
.page-hero{background:var(--brand-deep);color:#fff;position:relative;isolation:isolate;overflow:hidden}
.page-hero-media{position:absolute;inset:0;z-index:-2}
.page-hero-media img{width:100%;height:100%;object-fit:cover}
.page-hero::after{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(100deg,rgba(6,17,43,.94),rgba(10,31,77,.72))}
.page-hero-inner{padding:clamp(2.5rem,6vw,4.75rem) 0}
.page-hero h1{color:#fff;margin-bottom:.3em}
.page-hero .lede{color:#cfe6f3}

.breadcrumb{padding:.85rem 0;font-size:.92rem;background:var(--cream);border-bottom:1px solid var(--line)}
.breadcrumb ol{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:.4rem;align-items:center}
.breadcrumb li{margin:0;display:flex;align-items:center;gap:.4rem;color:var(--muted)}
.breadcrumb li+li::before{content:"/";color:#a9c2ce}
.breadcrumb a{color:var(--brand);text-decoration:none}
.breadcrumb a:hover{color:var(--accent);text-decoration:underline}

/* --- Forms -------------------------------------------------------------- */
.form-grid{display:grid;gap:1.15rem}
@media (min-width:700px){
  .form-grid{grid-template-columns:1fr 1fr}
  .form-grid .full{grid-column:1 / -1}
}
.field label{display:block;font-weight:700;margin-bottom:.35rem;color:var(--brand-deep)}
.field .hint{display:block;font-weight:400;color:var(--muted);font-size:.92rem;margin-top:.15rem}
.field input,.field select,.field textarea{
  width:100%;min-height:52px;padding:.75rem .9rem;
  font:inherit;color:var(--ink);background:#fff;
  border:1.5px solid #c3dae4;border-radius:5px;
}
.field textarea{min-height:150px;resize:vertical}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--brand);outline-offset:1px}
.field input[aria-invalid="true"],.field textarea[aria-invalid="true"]{border-color:var(--accent);background:#fff6fb}
.field-error{display:block;color:var(--accent-dark);font-weight:600;font-size:.94rem;margin-top:.3rem}
.hp-field{position:absolute!important;left:-9999px!important;width:1px;height:1px;overflow:hidden}
.consent{display:flex;gap:.65rem;align-items:flex-start}
.consent input{width:22px;height:22px;min-height:22px;margin-top:.2rem;flex:none}
.consent label{font-weight:400;color:var(--muted)}

.alert{padding:1.05rem 1.25rem;border-radius:6px;margin-bottom:1.5rem;border:1px solid}
.alert-ok{background:#eef8f2;border-color:#b8e0c9;color:#155f3c}
.alert-bad{background:#fdf0f8;border-color:#f2c2de;color:#8c165b}
.alert p:last-child{margin-bottom:0}

/* --- Contact panel ------------------------------------------------------ */
.contact-grid{display:grid;gap:2.5rem}
@media (min-width:960px){.contact-grid{grid-template-columns:1.15fr .85fr;gap:3.5rem}}
.info-card{background:var(--cream);border:1px solid var(--line);border-radius:8px;padding:1.6rem}
.info-card h3{margin-top:0}
.info-row{display:flex;gap:.75rem;align-items:flex-start;margin-bottom:1.1rem}
.info-row svg{width:20px;height:20px;fill:var(--accent);flex:none;margin-top:.25rem}
.info-row p{margin:0}
.map-embed{border:0;width:100%;aspect-ratio:16/10;border-radius:8px;background:var(--cream)}



/* --- Popup -------------------------------------------------------------- */
.popup-backdrop{
  position:fixed;inset:0;z-index:120;display:grid;place-items:center;padding:1rem;
  background:rgba(6,17,43,.72);
}
.popup-backdrop[hidden]{display:none}
.popup{
  background:#fff;border-radius:10px;max-width:32rem;width:100%;
  box-shadow:0 26px 70px rgba(0,0,0,.4);overflow:hidden;position:relative;
  max-height:90vh;overflow-y:auto;
}
.popup-close{
  position:absolute;top:.5rem;right:.5rem;width:44px;height:44px;
  background:rgba(255,255,255,.9);border:1px solid var(--line);border-radius:50%;
  font-size:1.5rem;line-height:1;color:var(--brand-deep);cursor:pointer;
}
.popup-close:hover{background:var(--accent);color:#fff;border-color:var(--accent)}
.popup-img{width:100%;aspect-ratio:16/9;object-fit:cover}
.popup-body{padding:1.6rem}
.popup-body h2{font-size:1.7rem}

/* --- 404 ---------------------------------------------------------------- */
.notfound{text-align:center;padding:clamp(3rem,9vw,6rem) 0}
.notfound-code{
  font-family:"Barlow Condensed",sans-serif;font-size:clamp(5rem,18vw,10rem);
  line-height:.85;color:var(--line);margin:0 0 .2em;
}
.notfound-links{list-style:none;padding:0;margin:1.75rem 0 0;
  display:flex;flex-wrap:wrap;gap:.6rem;justify-content:center}
.notfound-links a{
  display:inline-block;padding:.6rem 1.1rem;border:1px solid var(--line);
  border-radius:999px;text-decoration:none;color:var(--brand-deep);
}
.notfound-links a:hover{border-color:var(--accent);color:var(--accent)}

/* --- Tables ------------------------------------------------------------- */
/* A flex or grid child defaults to min-width:auto, which means it grows to fit
   its widest content instead of letting the scroller scroll. Without these the
   32rem table pushed the whole page sideways on a phone. */
.table-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;margin:1.25rem 0;
  min-width:0;max-width:100%}
.split > *,
.grid > *,
.contact-grid > *,
.info-row,
.info-row > *,
.footer-grid > *{min-width:0}
.data-table{width:100%;border-collapse:collapse;min-width:26rem}
.data-table th,.data-table td{padding:.7rem .85rem;border-bottom:1px solid var(--line);text-align:left}
.data-table th{background:var(--cream);font-family:"Barlow Condensed",sans-serif;
  text-transform:uppercase;letter-spacing:.05em;color:var(--brand-deep)}

/* --- Motion system ------------------------------------------------------

   Three rules, and they are not negotiable:

   1. Nothing is ever hidden without JavaScript. Every hidden-then-revealed
      state is behind `.js`, which an inline script in <head> sets before the
      first paint. If that script never runs, the page is simply a normal
      page.
   2. Nothing moves for somebody who asked it not to. `prefers-reduced-motion`
      turns the whole system off, and so does the `.reveal-off` class the
      script sets when it cannot observe.
   3. Nothing animates inside the Visual Editor. Transforms on content would
      throw off the editor's overlay positions.

   Everything below animates only `opacity` and `transform`, so none of it
   causes layout or paint work on the main thread.
   --------------------------------------------------------------------- */
/* NOTE: .reveal-ready is added by site.js only after the reveal observer is
   actually running, and a timer in the same module force-reveals everything
   if anything goes wrong. Never move these rules onto .js - a script error
   would then leave the page blank. */
.reveal-ready [data-reveal]{
  opacity:0;
  transform:translate3d(0,18px,0);
  transition:opacity .62s cubic-bezier(.22,.61,.36,1),
             transform .62s cubic-bezier(.22,.61,.36,1);
  will-change:opacity,transform;
}
.reveal-ready [data-reveal].is-revealed{
  opacity:1;
  transform:none;
  will-change:auto;
}
/* Escape hatches. Any one of these and the page is static. */
.reveal-off [data-reveal],
.ve-editing [data-reveal]{opacity:1 !important;transform:none !important;transition:none !important}

/* Directional variants, used by the auto-tagger for side-by-side blocks so a
   split section arrives from the two sides rather than both from below. */
.js [data-reveal="left"] {transform:translate3d(-26px,0,0)}
.js [data-reveal="right"]{transform:translate3d(26px,0,0)}
.js [data-reveal="zoom"] {transform:scale(.965)}
.reveal-ready [data-reveal].is-revealed{transform:none}

/* --- Hover and state motion --------------------------------------------
   The reveal above is one-shot; this is the everyday feel of the page.
   ---------------------------------------------------------------------- */

/* Photographs breathe very slightly under the cursor. The wrapper clips. */
.svc-media,.card-media,.split-media.framed,.gallery figure{overflow:hidden}
.svc-media img,.card-media img,.gallery figure img{
  transition:transform .7s cubic-bezier(.22,.61,.36,1);
}
.svc:hover .svc-media img,
.card:hover .card-media img{transform:scale(1.055)}

/* Service cards lift and their arrow steps forward. */
.svc{transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease}
.svc:hover{transform:translateY(-4px)}
.svc-link .ic,.card-link .ic{transition:transform .22s cubic-bezier(.22,.61,.36,1)}
.svc:hover .svc-link .ic,
.card:hover .card-link .ic,
.card-link:hover .ic{transform:translateX(5px)}

/* Buttons: a soft press, and a sheen that crosses once on hover. */
.btn{position:relative;overflow:hidden;transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .12s ease,box-shadow .15s ease}
.btn:hover{transform:translateY(-2px)}
.btn:active{transform:translateY(1px)}
.js .btn::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(105deg,transparent 38%,rgba(255,255,255,.28) 50%,transparent 62%);
  transform:translateX(-120%);
}
.js .btn:hover::after{transition:transform .62s ease;transform:translateX(120%)}
.reveal-off .btn::after{display:none}

/* The blade rule draws itself in when its heading arrives. */
.reveal-ready [data-reveal] .blade-rule svg path,
.reveal-ready [data-reveal] .blade-rule svg{transition:none}
.blade-rule svg path{
  stroke-dasharray:200;stroke-dashoffset:0;
}
.reveal-ready [data-reveal] .blade-rule svg path{stroke-dashoffset:200}
.reveal-ready [data-reveal].is-revealed .blade-rule svg path{
  stroke-dashoffset:0;
  transition:stroke-dashoffset .9s ease .18s;
}
.reveal-off .blade-rule svg path{stroke-dashoffset:0 !important;transition:none !important}

/* Navigation: the underline grows from the centre instead of snapping on. */
.nav-desktop ul > li > a{position:relative}
.nav-desktop ul > li > a::after{
  content:"";position:absolute;left:50%;right:50%;bottom:-6px;height:2px;
  background:var(--accent);border-radius:2px;
  transition:left .22s ease,right .22s ease;
}
.nav-desktop ul > li > a:hover::after,
.nav-desktop ul > li > a[aria-current="page"]::after{left:0;right:0}

/* Trust strip items rise in sequence. */
.trust-item{transition:transform .2s ease}
.trust-item:hover{transform:translateY(-3px)}

/* Price cards: the figure settles a beat after the card. */
.reveal-ready [data-reveal] .price-figure{
  opacity:0;transform:translate3d(0,10px,0);
  transition:opacity .5s ease .2s,transform .5s ease .2s;
}
.reveal-ready [data-reveal].is-revealed .price-figure{opacity:1;transform:none}
.reveal-off .price-figure{opacity:1 !important;transform:none !important}

/* FAQ disclosure arrow. */
.faq-item summary::after{transition:transform .24s ease}
.faq-item[open] summary::after{transform:rotate(180deg)}

/* Service-area links slide their arrow. */
.area-list a .ic{transition:transform .22s ease}
.area-list a:hover .ic{transform:translateX(5px)}
/* --- Motion policy ------------------------------------------------------

   The browser's prefers-reduced-motion setting deliberately does NOT switch
   this site's animation off. That is the owner's decision, made explicitly,
   and it is not an oversight.

   What replaces it is a control a visitor can actually find: the
   accessibility widget in the bottom-right corner carries a Pause Motion
   tool that stops every animation on the page. That is more discoverable
   than a setting buried three levels into an operating system.

   To reverse the policy, drop the impossible min-width below.
   ---------------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce) and (min-width:99999px){
  *,*::before,*::after{
    animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;scroll-behavior:auto!important;
  }
  /* The video is already gated in JS: with reduced motion it never starts
     on its own, and [hidden] keeps it out of the way until it does. Do NOT
     add display:none here - a visitor who deliberately presses play has
     asked for it, and hiding it then would just look broken. */
}

/* --- Right-click actions ------------------------------------------------

   A small translucent panel offering the three things a visitor actually
   wants from this business: telephone, message, portal. It replaces the
   browser menu only on ordinary page area - never on a form field, never on
   a link or an image, and never when Shift is held - so nothing a visitor
   normally right-clicks for is taken away from them.
   --------------------------------------------------------------------- */
.tr-menu{
  position:fixed;z-index:150;                 /* over the popup, under the skip link */
  min-width:15.5rem;max-width:min(19rem,92vw);
  margin:0;padding:.38rem;
  list-style:none;
  background:rgba(10,26,60,.8);
  border:1px solid rgba(255,255,255,.2);
  border-radius:8px;
  box-shadow:0 24px 60px rgba(3,10,28,.5),0 2px 0 rgba(255,255,255,.06) inset;
  color:#fff;
  opacity:0;transform:scale(.97) translateY(-4px);transform-origin:top left;
  transition:opacity .12s ease,transform .12s ease;
}
@supports ((backdrop-filter:blur(4px)) or (-webkit-backdrop-filter:blur(4px))){
  .tr-menu{
    -webkit-backdrop-filter:blur(16px) saturate(150%);
    backdrop-filter:blur(16px) saturate(150%);
    background:rgba(10,26,60,.62);
  }
}
.tr-menu[hidden]{display:none}
.tr-menu.is-open{opacity:1;transform:none}

.tr-menu-head{
  display:flex;align-items:center;gap:.5rem;
  padding:.45rem .6rem .5rem;margin:0 0 .3rem;
  border-bottom:1px solid rgba(255,255,255,.16);
  font-size:.72rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:#a9d2e8;
}
.tr-menu-head img{width:20px;height:auto;flex:none;border-radius:2px;background:#fff;padding:1px}

.tr-menu li{margin:0}
.tr-menu a{
  display:flex;align-items:center;gap:.7rem;
  padding:.62rem .65rem;border-radius:5px;
  color:#fff;text-decoration:none;font-size:.98rem;line-height:1.25;
}
.tr-menu a .ic{width:17px;height:17px;fill:#ff8ace;flex:none}
.tr-menu a small{display:block;color:#a9d2e8;font-size:.79rem;font-weight:400}
.tr-menu a b{font-weight:700}
.tr-menu a:hover,
.tr-menu a:focus-visible{background:rgba(255,255,255,.14);color:#fff;outline:none}
.tr-menu a:focus-visible{box-shadow:inset 0 0 0 2px var(--accent)}
.tr-menu .tr-menu-ext{margin-left:auto;opacity:.6}
.tr-menu .tr-menu-ext svg{width:13px;height:13px;fill:currentColor;display:block}

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

/* Visual Editor affordances live in assets/css/ve-frame.css, which loads only
   for a signed-in editor. Nothing for them is shipped to a visitor. */

/* An element switched off in the Visual Editor. The attribute is used rather
   than deleting the node, so stored editor paths keep resolving. */
[hidden]{display:none !important}

/* --- Buttons on a phone -------------------------------------------------
   Stacked pairs of buttons were coming out at whatever width their label
   happened to need, which reads as ragged rather than as a set. Below this
   width they are a stack of equal blocks, and each one clears the 44px
   minimum touch target comfortably.
   --------------------------------------------------------------------- */
@media (max-width:599.98px){
  .btn-row,
  .cta-actions{flex-direction:column;align-items:stretch;width:100%}
  .btn-row .btn,
  .cta-actions .btn{width:100%;justify-content:center}
  /* Rows that were deliberately centred stay centred. */
  .btn-row.center{align-items:stretch}
}

/* Autoplay was refused, so the control stops being a quiet affordance and
   becomes the thing that turns a still into a video. Set by site.js. */
.hero-video-toggle.is-offered{opacity:1}

/* --- Hero badge ---------------------------------------------------------
   The shield, filling the right half of the hero on a wide screen. Below
   960px the hero is a single column and the logo already sits at the top of
   the copy (.hero-mark), so this one stands down rather than repeating it.

   Decorative only: the header carries the real described logo.
   --------------------------------------------------------------------- */
.hero-badge{display:none}
@media (min-width:960px){
  .hero-badge{
    display:grid;place-items:center;position:relative;min-width:0;
    /* Lifted off the vertical centre so it never lands on the Ronnie Trammell watermark
       burned into the bottom-right of the commercial footage. */
    align-self:center;
    transform:translateY(-6%);
  }
  /* A soft pool of brand light, so the shield reads against busy footage
     instead of getting lost in a hedge. */
  .hero-badge::before{
    content:"";position:absolute;z-index:0;
    width:min(34rem,128%);aspect-ratio:1;border-radius:50%;
    background:radial-gradient(circle,
      rgba(27,109,159,.46) 0%,rgba(12,68,104,.26) 44%,transparent 70%);
  }
  .hero-badge img{
    position:relative;z-index:1;
    width:clamp(200px,21vw,310px);height:auto;display:block;
    filter:drop-shadow(0 24px 48px rgba(4,14,32,.66));
  }
  /* Barely-there drift, on the same clock as the background so the two do not
     beat against each other. Damped by the global reduced-motion block. */
  .js .hero-badge img{animation:badgeFloat 9s ease-in-out infinite alternate}
}
@keyframes badgeFloat{
  from{transform:translate3d(0,-6px,0)}
  to  {transform:translate3d(0,6px,0)}
}

/* --- House typography ---------------------------------------------------

   Two conventions the owner asked for, applied where they read as deliberate
   and withheld where they would read as a mistake.

   FIRST-LINE INDENT. Done with text-indent, not with spaces in the markup:
   HTML collapses whitespace, so leading spaces in a template render as
   nothing at all. Applied to running prose only. Card copy, ledes, notices,
   form help, FAQ answers, table cells, captions and the footer are interface
   rather than reading, and an indent there just looks like a broken margin.

   DROP CAPS. Only on the opening paragraph of a genuine run of prose, which
   in practice means the paragraph immediately after an h2 inside a .prose
   block. A drop cap on a two-line card is a gimmick; on a four-paragraph
   passage it is a signpost.
   --------------------------------------------------------------------- */

/* Running prose in the two-column feature blocks and measured columns, which
   is where the service, city and interior pages carry their body copy. */
.split > div > p:not(.lede):not(.tag):not(.eyebrow):not(.frame-note):not(.price-note),
.measure > p:not(.lede):not(.tag):not(.eyebrow),
.page-body > p:not(.lede):not(.tag):not(.eyebrow){
  text-indent:2ch;
}
@media (max-width:559.98px){
  .split > div > p:not(.lede):not(.tag):not(.eyebrow):not(.frame-note):not(.price-note),
  .measure > p:not(.lede):not(.tag):not(.eyebrow),
  .page-body > p:not(.lede):not(.tag):not(.eyebrow){text-indent:1.5ch}
}

/* The paragraph that carries a drop cap hangs flush: a floated capital and a
   first-line indent fight each other for the same space.

   These selectors repeat the :not() chains above on purpose. Without them
   the reset is less specific than the indent rule and silently loses, which
   is exactly what happened the first time: the cap floated AND the first
   line was pushed 2ch to the right of it. Keep the shapes matched. */
.prose > h2 + p:not(.tag):not(.lede):not(.eyebrow),
.split > div > h2 + p:not(.lede):not(.tag):not(.eyebrow):not(.frame-note):not(.price-note),
.prose--cap > p.lede,
.prose--cap > h2 + p{text-indent:0}

/* Drop cap on the opening paragraph of a prose run. The existing rule covers
   .lede openers; this covers the ones that open with a plain paragraph, which
   is how the city pages are built. */
.prose--cap > h2 + p::first-letter{
  float:left;
  font-family:"Barlow Condensed",Impact,sans-serif;
  font-weight:700;
  font-size:3.5em;
  line-height:.78;
  margin:.06em .1em 0 0;
  color:var(--accent);
}
@media (max-width:559.98px){
  .prose--cap > h2 + p::first-letter{font-size:2.9em}
}
/* Never two caps in one block: if the block already has a .lede opener, the
   h2 + p rule stands down. */
.prose--cap > p.lede ~ h2 + p::first-letter{
  float:none;font-size:inherit;line-height:inherit;margin:0;color:inherit;
  font-family:inherit;font-weight:inherit;
}

/* =========================================================================
   SHOWPIECE MOTION
   -------------------------------------------------------------------------
   The big set pieces: the opening curtain, the hero entrance, the scroll
   rail, and the glow and lift that the cards and buttons carry.

   Rules this layer obeys, all of them for a reason:

   1. Only transform, opacity, filter and clip-path are animated. Nothing here
      animates width, height, top or left, so none of it triggers layout on
      the main thread and none of it can jank a scroll.
   2. Everything that starts hidden hangs off .js, and the curtain removes
      itself unconditionally. If a script fails, the page is a normal page.
   3. The curtain never gates content. It is an overlay over a fully rendered
      document, aria-hidden, and it is gone in about a second.
   4. The accessibility widget's Pause Motion tool stops all of it, because it
      sets transition and animation to none on every element.
   ========================================================================= */

/* --- The opening curtain ------------------------------------------------ */
.splash{
  position:fixed;inset:0;z-index:9000;
  display:none;                       /* only .js turns it on */
  place-items:center;
  background:
    radial-gradient(120% 100% at 50% 0%,#1b6d9f 0%,transparent 62%),
    linear-gradient(170deg,var(--brand-deep) 0%,#082033 100%);
  overflow:hidden;
}
/* The curtain lifts on a keyframe, not on a callback. Tested with site.js
   removed: it still clears. JavaScript only ever cuts it short. */
.js .splash{display:grid;animation:splashAuto 1.95s cubic-bezier(.7,0,.32,1) both}
.js .splash.is-done{display:none}
@keyframes splashAuto{
  0%,52%{clip-path:inset(0 0 0 0);opacity:1;visibility:visible}
  99%   {clip-path:inset(0 0 100% 0);opacity:0}
  100%  {clip-path:inset(0 0 100% 0);opacity:0;visibility:hidden;pointer-events:none}
}

.splash-inner{
  display:flex;align-items:center;gap:clamp(.9rem,2.4vw,1.6rem);
  padding:1rem;position:relative;z-index:2;
}
.splash-mark{
  display:block;width:clamp(78px,12vw,124px);flex:none;
  filter:drop-shadow(0 18px 40px rgba(0,0,0,.55));
  animation:splashMark .9s cubic-bezier(.16,.9,.3,1) both;
}
.splash-mark img{width:100%;height:auto;display:block}
.splash-word{display:flex;flex-direction:column;gap:.25rem;overflow:hidden}
.splash-word b{
  font-family:"Barlow Condensed",sans-serif;font-weight:700;
  font-size:clamp(1.9rem,5.6vw,3.4rem);line-height:1;letter-spacing:.01em;
  text-transform:uppercase;color:#fff;
  animation:splashLine .8s cubic-bezier(.16,.9,.3,1) .12s both;
}
.splash-word i{
  font-style:normal;
  font-family:"Source Sans 3",sans-serif;font-weight:600;
  font-size:clamp(.72rem,1.7vw,.94rem);letter-spacing:.26em;text-transform:uppercase;
  color:#ff8ace;
  animation:splashLine .8s cubic-bezier(.16,.9,.3,1) .24s both;
}
/* The wipe that takes the curtain away. */
.splash-sweep{
  position:absolute;inset:0;z-index:3;pointer-events:none;
  background:linear-gradient(100deg,transparent 34%,rgba(255,255,255,.16) 50%,transparent 66%);
  transform:translateX(-120%);
}
.splash .splash-sweep{animation:splashSweep 1.1s ease-in .5s both}
/* Cutting it short when the visitor clicks or scrolls. */
.splash.is-leaving{animation:splashLift .45s cubic-bezier(.7,0,.32,1) forwards}

@keyframes splashMark{
  from{opacity:0;transform:translate3d(0,14px,0) scale(.82) rotate(-7deg)}
  to  {opacity:1;transform:none}
}
@keyframes splashLine{
  from{opacity:0;transform:translate3d(0,110%,0)}
  to  {opacity:1;transform:none}
}
@keyframes splashSweep{
  from{transform:translateX(-120%)}
  to  {transform:translateX(120%)}
}
@keyframes splashLift{
  to{clip-path:inset(0 0 100% 0);opacity:.9}
}

/* --- Scroll progress rail ----------------------------------------------- */
.scroll-rail{
  position:fixed;left:0;right:0;top:0;height:3px;z-index:130;
  pointer-events:none;background:transparent;
}
.scroll-rail span{
  display:block;height:100%;width:100%;
  transform:scaleX(0);transform-origin:0 50%;
  background:linear-gradient(90deg,var(--brand-soft),var(--accent));
  box-shadow:0 0 12px rgba(209,58,146,.55);
}
html:not(.js) .scroll-rail{display:none}

/* --- Hero entrance ------------------------------------------------------
   The hero is above the fold, so it is not driven by the scroll observer.
   site.js adds .is-lit once the curtain is out of the way and runs this
   sequence, which is why the delays are on the lit state rather than on load.
   ---------------------------------------------------------------------- */
/* The hero entrance.

   Keyed off .js with `both` fill, so every one of these ALWAYS ends visible
   on its own. There is no class to wait for and nothing to go wrong: if
   site.js never runs, the sequence still plays and still finishes.

   Two timings. By default the delays sit behind the curtain, so the hero is
   mid-move as it lifts. On a repeat view site.js adds .splash-skipped to
   <html> and the same sequence runs immediately instead.
   ---------------------------------------------------------------------- */
.js .hero .hero-mark   {animation:heroRise .85s cubic-bezier(.16,.9,.3,1) 1.02s both}
.js .hero .tag         {animation:heroRise .80s cubic-bezier(.16,.9,.3,1) 1.08s both}
.js .hero h1           {animation:heroRise .90s cubic-bezier(.16,.9,.3,1) 1.16s both}
.js .hero h1 > span    {animation:heroRise .90s cubic-bezier(.16,.9,.3,1) 1.30s both}
.js .hero .hero-lede   {animation:heroRise .85s cubic-bezier(.16,.9,.3,1) 1.38s both}
.js .hero .hero-marks  {animation:heroRise .85s cubic-bezier(.16,.9,.3,1) 1.48s both}
.js .hero .hero-actions{animation:heroPop  .70s cubic-bezier(.2,1.5,.4,1)  1.58s both}

/* Seen the curtain already this session: same sequence, no waiting. */
.js.splash-skipped .hero .hero-mark   {animation-delay:.04s}
.js.splash-skipped .hero .tag         {animation-delay:.10s}
.js.splash-skipped .hero h1           {animation-delay:.18s}
.js.splash-skipped .hero h1 > span    {animation-delay:.32s}
.js.splash-skipped .hero .hero-lede   {animation-delay:.40s}
.js.splash-skipped .hero .hero-marks  {animation-delay:.50s}
.js.splash-skipped .hero .hero-actions{animation-delay:.60s}

@keyframes heroRise{
  from{opacity:0;transform:translate3d(0,26px,0)}
  to  {opacity:1;transform:none}
}
@keyframes heroPop{
  0%  {opacity:0;transform:translate3d(0,18px,0) scale(.97)}
  100%{opacity:1;transform:none}
}
/* The badge arrives with a turn and keeps a slow brand glow. */
@media (min-width:960px){
  .js .hero-badge{animation:badgeArrive 1.1s cubic-bezier(.16,.9,.3,1) 1.34s both}
  .js.splash-skipped .hero-badge{animation-delay:.36s}
  .js .hero-badge::before{animation:badgeGlow 5.5s ease-in-out 2.6s infinite}
}
@keyframes badgeArrive{
  from{opacity:0;transform:translate3d(0,34px,0) scale(.86) rotate(-6deg)}
  to  {opacity:1;transform:translateY(-6%)}
}
@keyframes badgeGlow{
  0%,100%{opacity:.75;transform:scale(1)}
  50%    {opacity:1;transform:scale(1.06)}
}


/* --- Glow and lift on the interface ------------------------------------- */

/* Primary actions carry a brand glow that intensifies on hover. */
.btn-primary,.btn-call{box-shadow:0 12px 26px rgba(166,32,110,.34),0 0 0 0 rgba(209,58,146,.5)}
.btn-primary:hover,.btn-call:hover{
  box-shadow:0 18px 34px rgba(166,32,110,.44),0 0 0 6px rgba(209,58,146,.14);
}
.btn-brand:hover{box-shadow:0 16px 30px rgba(12,68,104,.34),0 0 0 6px rgba(27,109,159,.14)}

/* Cards: a lifted edge and a ring of brand light. */
.svc,.card,.price-card{
  transition:transform .22s cubic-bezier(.16,.9,.3,1),
             box-shadow .22s ease,
             border-color .22s ease;
}
.svc:hover,.card:hover,.price-card:hover{
  transform:translateY(-6px);
  border-color:#8fbed6;
  box-shadow:0 22px 44px rgba(10,31,77,.16),0 0 0 4px rgba(27,109,159,.10);
}

/* The service card picture gets a slow push in, and a wash of brand colour
   crossing it once, so the grid feels alive under the cursor without every
   card shouting at the same time. */
.svc-media,.card-media{position:relative}
.svc-media::after,.card-media::after{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(105deg,transparent 36%,rgba(255,255,255,.30) 50%,transparent 64%);
  transform:translateX(-130%);
}
.svc:hover .svc-media::after,
.card:hover .card-media::after{transition:transform .8s ease;transform:translateX(130%)}

/* Gallery frames: brand ring plus a lift. */
.gallery figure{transition:transform .3s cubic-bezier(.16,.9,.3,1),box-shadow .3s ease}
.gallery figure:hover{transform:translateY(-5px) scale(1.012);
  box-shadow:0 20px 40px rgba(10,31,77,.24),0 0 0 3px rgba(209,58,146,.35)}

/* Trust strip: each fact lifts and its rule brightens. */
.trust-item{transition:transform .22s cubic-bezier(.16,.9,.3,1)}
.trust-item:hover{transform:translateY(-4px)}
.trust-item:hover strong{text-shadow:0 0 18px rgba(255,255,255,.55)}

/* The area list reads as a route: each stop lights up in turn. */
.area-list a{transition:transform .2s ease,color .2s ease}
.area-list a:hover{transform:translateX(4px)}

/* Section headings get a soft brand glow behind them as they arrive. */
.sec-head.center h2{position:relative}
.sec-head.center h2::after{
  content:"";position:absolute;left:50%;top:50%;z-index:-1;
  width:min(30rem,90%);height:130%;
  transform:translate(-50%,-50%) scale(.6);
  background:radial-gradient(closest-side,rgba(27,109,159,.16),transparent 70%);
  opacity:0;
}
[data-reveal].is-revealed .sec-head.center h2::after,
.sec-head.center[data-reveal].is-revealed h2::after{
  transition:opacity .9s ease .15s,transform .9s ease .15s;
  opacity:1;transform:translate(-50%,-50%) scale(1);
}

/* Price figures land with a small overshoot, which is the whole reason to
   look at a price card. */
.reveal-ready [data-reveal].is-revealed .price-figure{
  animation:figurePop .55s cubic-bezier(.2,1.6,.4,1) .18s both;
}
@keyframes figurePop{
  0%  {opacity:0;transform:translate3d(0,12px,0) scale(.9)}
  100%{opacity:1;transform:none}
}

/* --- Curtain and rail on a phone ---------------------------------------- */
@media (max-width:559.98px){
  .splash-inner{flex-direction:column;text-align:center;gap:.9rem}
  .splash-word{align-items:center}
  .scroll-rail{height:2px}
}

/* --- Mobile drawer: the services group ----------------------------------
   The drawer used to print all twelve services inline, every time it opened,
   so the first thing a visitor saw was a wall of links and Contact was three
   screens down. The group is now collapsible.

   It ships OPEN in the markup and site.js collapses it, which is the same
   progressive-enhancement shape the footer groups use: with scripting off the
   drawer is the full list and nothing is unreachable.
   ---------------------------------------------------------------------- */
.nav-mobile-row{
  display:flex;align-items:center;
  border-bottom:1px solid var(--line);
}
.nav-mobile-row > a{flex:1 1 auto;min-width:0;border-bottom:0}
.nav-mobile-sub-toggle{
  flex:none;width:56px;align-self:stretch;
  display:grid;place-items:center;
  background:none;border:0;border-left:1px solid var(--line);
  color:var(--brand-deep);cursor:pointer;
}
.nav-mobile-sub-toggle:hover{background:var(--cream);color:var(--accent)}
.nav-mobile-sub-toggle svg{
  width:20px;height:20px;fill:currentColor;
  transition:transform .2s ease;
}
.nav-mobile-sub-toggle[aria-expanded="true"] svg{transform:rotate(180deg)}
.nav-mobile-subwrap{margin:0}
.nav-mobile-sub{list-style:none;margin:0;padding:0;background:var(--cream)}
.nav-mobile-sub[hidden]{display:none}

/* --- Legacy fragment anchors --------------------------------------------
   Landing points for #LawnCare, #AboutUs and the rest of the fragments the
   previous website used. Empty, invisible, and out of the accessibility tree:
   they exist so that a link somebody bookmarked in 2019 still puts them in
   front of the right block. The scroll margin keeps the target clear of the
   sticky header. */
.legacy-anchor{
  /* OUT OF FLOW, and that is the whole point.
     These are zero-size markers, but a zero-size element is still a grid item
     and still a flex item. Dropped in as the first child of the lead service
     card - which is a two column grid - it took the first cell, pushed the
     picture into the second, and wrapped the wording onto a row of its own.
     The card rendered as an empty white block with the photograph beside it.
     Absolutely positioned with no offsets, it keeps the exact spot it would
     have occupied, so a #fragment still scrolls to the right place, and it
     cannot take part in any layout again. */
  position:absolute;
  width:0;height:0;overflow:hidden;
  scroll-margin-top:120px;
}

/* --- Service page long-form blocks --------------------------------------
   The educational sections on each service page. One measured column of
   running text, because two columns of body copy is a magazine trick that
   does not survive a phone, with an optional tick list, table or card row
   underneath. */
.svc-guide .sec-head{max-width:44rem}
.guide-body{max-width:70ch}
.guide-body > p{margin-bottom:1.05rem}
.guide-body > p:last-child{margin-bottom:0}
.guide-body > .lede{margin-bottom:1.3rem}
.guide-body strong{color:var(--brand-deep)}

/* Links inside the running copy have to be obvious as links without turning
   the paragraph into a rash of blue. Brand color, underlined, and the
   underline thickens rather than the color changing on hover. */
.guide-body a,
.guide-note a{
  color:var(--brand);
  text-decoration:underline;
  text-decoration-thickness:1px;
  text-underline-offset:.18em;
  font-weight:600;
}
.guide-body a:hover,
.guide-note a:hover{color:var(--accent-dark);text-decoration-thickness:2px}
.guide-body a:focus-visible,
.guide-note a:focus-visible{
  outline:3px solid var(--accent);outline-offset:2px;border-radius:2px;
}

.ticks{list-style:none;padding:0;margin:1.35rem 0 0;display:grid;gap:.62rem}
.ticks li{display:flex;gap:.65rem;align-items:flex-start}
.ticks svg{width:19px;height:19px;fill:var(--accent);flex:none;margin-top:.26rem}

.guide-cards{margin-top:2.1rem}
.guide-cards .card-body{padding:1.15rem 1.25rem 1.3rem}
.guide-cards h3{font-size:1.12rem}
.guide-cards p{margin-bottom:0}

.guide-table{min-width:34rem}
.guide-table th[scope="row"]{
  background:transparent;font-family:inherit;font-weight:700;
  color:var(--brand-deep);white-space:normal;
}
.guide-note{margin:.35rem 0 0;color:var(--muted);font-size:.94rem}

@media (min-width:900px){
  .svc-guide .guide-body{font-size:1.03rem}
}

/* --- White text always carries a shadow ---------------------------------
   Site rule: anything set in white is sitting on photography, video, or a
   deep brand colour, and it gets a shadow underneath it. Over the hero video
   that is doing contrast work - a bright frame can pass behind any word at
   any moment - and over the flat panels it stops the type looking pasted on.

   Kept to a soft, tight shadow rather than a hard offset: the point is to
   hold an edge against whatever is behind it, not to look embossed.
   --------------------------------------------------------------------- */
:root{
  --ink-shadow:0 1px 2px rgba(4,18,30,.45), 0 2px 10px rgba(4,18,30,.28);
  --ink-shadow-soft:0 1px 2px rgba(4,18,30,.34);
}

/* Over moving footage and photography: the strongest of the three. */
.hero h1,
.hero .hero-lede,
.hero .tag--onDark,
.hero-marks b,
.hero-marks span,
.hero-video-toggle,
.page-hero h1,
.page-hero .lede,
.page-hero .eyebrow{
  text-shadow:var(--ink-shadow);
}

/* Over flat brand panels, where there is no photograph to fight but white
   type on saturated blue still reads better with a little weight under it. */
.section--brand h2,
.section--brand h3,
.method h2,
.method .lede,
.method-points strong,
.cta-band h2,
.cta-band p,
.footer-address strong,
.footer-address a,
.footer-brand .footer-name,
.footer-today-status,
.footer-today-hours strong,
.hours-compact dd,
.splash-word b,
.splash-word i{
  text-shadow:var(--ink-shadow-soft);
}

/* Buttons are small, bold and sit on their own solid fill, so they take the
   softer shadow only - the heavy one blurs the label at button sizes. */
.btn-primary,
.btn-on-dark,
.btn-brand,
.btn-call-label,
.btn-call-num{
  text-shadow:var(--ink-shadow-soft);
}
