/* Rainwater Services — site styles
   Tokens live in tokens.css, which MUST be linked before this file.
   Edit them through styleguide.html. */

*,*::before,*::after{box-sizing:inherit}
html{scroll-behavior:smooth;scroll-padding-top:calc(90px + env(safe-area-inset-top,0px));-webkit-text-size-adjust:100%}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}*{transition:none!important;animation:none!important}}
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--body);font-size:var(--body-size);line-height:var(--body-leading);-webkit-font-smoothing:antialiased}
img{max-width:100%;display:block}
a{color:var(--deep);text-decoration-thickness:1px;text-underline-offset:3px}
a:focus-visible,button:focus-visible{outline:3px solid var(--green-bright);outline-offset:2px;border-radius:2px}
h1,h2,h3,h4{font-family:var(--head);font-weight:700;line-height:1.14;letter-spacing:var(--head-tracking);margin:0 0 .5em}
h1{font-size:clamp(2.05rem,5.4vw,3.35rem)}
h2{font-size:clamp(1.55rem,3.4vw,2.2rem)}
h3{font-size:1.2rem;font-weight:600;letter-spacing:-.01em}
h4{font-size:1.02rem;font-weight:600;letter-spacing:0}
p{margin:0 0 1.1em;max-width:68ch}
.wrap{max-width:var(--wrap);margin:0 auto;padding:0 22px}
.narrow{max-width:760px}
.skip{position:absolute;left:-9999px;top:0;background:#fff;color:var(--deep);padding:12px 18px;z-index:200}
.skip:focus{left:8px;top:8px}
section.band{padding:var(--band-pad) 0}
section.band.tight{padding:var(--band-pad-tight) 0}
.tinted{background:var(--sk-band-alt)}
/* A band that carries the section rather than sitting behind it. In Depth it
   is deep ocean; in Field it is the same white as everything else and the
   section is held by its rule instead. */
.band.feature,.feature{background:var(--sk-feature-bg);color:var(--sk-feature-ink)}

/* ---------- header ---------- */
.topbar{background:var(--abyss);color:#BFD9E4;font-size:.8rem}
/* Two gaps, not one. The 16px separates the service area from the links
   while they share a row; once they wrap on a phone the same value
   became the row gap and pushed the bar to 58px, most of it empty. */
.topbar .wrap{display:flex;justify-content:space-between;align-items:center;
  gap:3px 16px;min-height:34px;flex-wrap:wrap}
/* With the service-area line switched off the strip holds one item, and
   space-between would park it on the left. Pushed right instead, where it has
   always been. */
.topbar .wrap > span:only-child{margin-left:auto}
.topbar a{color:#BFD9E4;text-decoration:none}
.topbar a:hover{color:#fff;text-decoration:underline}
header.site{position:sticky;top:env(safe-area-inset-top,0px);z-index:60;background:rgba(255,255,255,.97);border-bottom:1px solid var(--rule);backdrop-filter:saturate(1.4) blur(6px)}
.bar{display:flex;align-items:center;gap:18px;min-height:90px}
.brand{display:flex;align-items:center;flex:0 0 auto;text-decoration:none}
.brand picture{display:block;line-height:0}
/* One height at every width, and the bar is sized from it rather than the
   other way round. The header used to step 64px on a phone, 40px on a tablet
   and 46px on a desktop - the phone showing the LARGEST logo, which is the
   step that gave it away. Those three numbers were set when the phone slot
   held a different file; once one file serves every width they were three
   sizes of the same artwork with nothing choosing between them.

   width:auto, not a fixed width. The old phone rule was `width:64px`, which
   was only ever correct because the file in that slot was the square vertical
   lockup; a lockup of any other shape would have been squashed to fit -
   silently, and only on phones, which is the hardest place to notice it. The
   height governs and the width follows: this file renders 136px wide, which
   leaves 86px beside the menu button at 360px. tools/build-logos.py warns
   when the file named in brand.json is wide enough to close that gap. */
.brand img{height:64px;width:auto}
nav.main{margin-left:auto;display:flex;gap:22px;align-items:center}
nav.main a{font-size:.895rem;color:var(--ink);text-decoration:none;padding:6px 0;border-bottom:2px solid transparent}
nav.main a:hover{border-bottom-color:var(--green-bright)}
nav.main a[aria-current="page"]{border-bottom-color:var(--ocean);font-weight:600}
.btn{display:inline-block;font-family:var(--head);font-weight:600;font-size:.88rem;padding:12px 20px;border-radius:var(--sk-btn-radius);text-decoration:none;border:2px solid transparent;cursor:pointer;transition:background .18s,color .18s}
/* nav.main a sets color and outranks a single class, so the header's own
   primary button lost its white text and rendered --ink on --ocean: 3.24:1,
   under AA, on every page. Matching that specificity restores what this
   rule already intended (4.79:1). */
nav.main a.btn-primary,.btn-primary{background:var(--sk-cta-bg);color:var(--sk-cta-ink)}
.btn-primary:hover{filter:brightness(.93)}
.btn-ghost{border-color:var(--ocean);color:var(--ocean);background:#fff}
.btn-ghost:hover{background:var(--tint)}
.btn-light{background:var(--sk-cta-bg);color:var(--sk-cta-ink)}
/* The second hero button. It used to be an inline style written for a photo
   background; it reads its colour from the design now, so it works on deep
   ocean and on white without being two different buttons. */
.btn-outline{border:2px solid var(--sk-hero-ink);color:var(--sk-hero-ink);background:none}
.btn-outline:hover{background:var(--sk-hero-ink);color:var(--sk-hero-bg)}
.btn-light:hover{background:#DDEDF5}
.btn-outline-light{border:2px solid rgba(255,255,255,.55);color:#fff}
.btn-outline-light:hover{background:rgba(255,255,255,.12)}
.btn-lg{padding:15px 28px;font-size:.96rem}
.menu-btn{display:none;align-items:center;gap:8px;margin-left:auto;background:none;border:1px solid var(--rule);border-radius:var(--sk-btn-radius);padding:9px 12px 9px 11px;font-family:var(--head);font-weight:600;font-size:.85rem;color:var(--ink)}
/* The bars beside the word, not instead of it. The icon alone is read fluently
   by people who use apps all day and less well by everyone else, and this
   site's visitors are Pinellas homeowners - older than the average web
   audience, and the group a bare glyph serves worst. The header has 184px of
   spare gap at 360px, so the word costs nothing to keep.

   Three lines drawn on top of each other and pushed apart, so opening only
   has to stop pushing and rotate two of them. No second icon, no swapped
   markup, and the accessible name stays "Menu" in both states - the svg is
   aria-hidden and aria-expanded carries the state. The transition is covered
   by the global reduced-motion rule at the top of this file. */
.menu-btn svg{width:18px;height:18px;flex:0 0 auto;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round}
.menu-btn svg path{transform-box:view-box;transform-origin:center;
  transition:transform .18s ease,opacity .18s ease}
.menu-btn svg .t{transform:translateY(-5.5px)}
.menu-btn svg .b{transform:translateY(5.5px)}
.menu-btn[aria-expanded="true"] svg .t{transform:rotate(45deg)}
.menu-btn[aria-expanded="true"] svg .m{opacity:0}
.menu-btn[aria-expanded="true"] svg .b{transform:rotate(-45deg)}
@media(max-width:1020px){
  nav.main{display:none;width:100%;flex-direction:column;align-items:stretch;gap:0;padding-bottom:14px}
  nav.main.open{display:flex}
  nav.main a{padding:13px 2px;border-bottom:1px solid var(--rule)}
  nav.main .btn{margin-top:12px;text-align:center;border-bottom:none}
  .menu-btn{display:flex}
  /* Shorter than the desktop bar because the nav is behind a button here,
     so the bar holds two things instead of nine. The logo is the same 64px
     it is everywhere else. */
  .bar{flex-wrap:wrap;min-height:78px}
}
/* There is no CSS at the 620px breakpoint any more. It survives in
   brand.json as `compact_below`, which chooses WHICH FILE the header loads
   below it - a real switch, just not one that needs its own size, since the
   rules above set the box from the height and let the width follow. It ships
   pointing at the same file as `wide`, so nothing swaps today. */

/* ---------- heroes ---------- */
/* The overlay is tuned against hero-waterfront.jpg specifically. The left
   edge is light because the palm fronds already darken that corner; the
   bottom veil is there because the license line sits over sunlit turf; the
   arcs are off because the photograph contains real spray. These are not
   round numbers and should not be made into round numbers.

   The veil stays inline rather than becoming a token: its first stop is the
   same colour at zero alpha, and substituting `transparent` for it shifts
   the ramp by 1/255. One token cannot express both stops without that drift,
   and two tokens for one gradient is not worth the cost. */
/* The homepage hero. The photograph is a background again, behind the
   headline, in whichever design asks for one - Depth does, Field sets
   --sk-hero-photo to none and keeps its flat white band.

   It was an <img> beside the headline for a year, for a good reason: the
   photograph then was a bright sky, and white text on it could not reach AA
   whatever the scrim. The photograph is now a white truck against foliage,
   which is a different problem with a different answer. Nothing here is
   trusted on that basis, though - tools/check-hero-photo.py measures the
   file through this exact scrim on every build.

   Two layers, not one background shorthand: ::before carries the photo so
   the brightness filter reaches it without touching the text, and ::after
   carries the scrim above it. A filter on .hero itself would dim the
   headline it is there to protect. */
.hero{background:var(--sk-hero-bg);color:var(--sk-hero-ink);
  position:relative;overflow:hidden;isolation:isolate}
.hero::before,.hero::after{content:"";position:absolute;inset:0}
.hero::before{background-image:var(--sk-hero-photo,none);background-size:cover;
  background-position:var(--ph-home-pos,center);
  filter:brightness(var(--sk-hero-photo-light,1));z-index:-2}
.hero::after{background:var(--sk-hero-scrim,none);z-index:-1}
.hero .wrap{position:relative;z-index:1;padding-top:66px;padding-bottom:70px}
/* The headline sits on a panel rather than straight on the photograph, which
   is what lets the photograph run unwashed. Every value is a token: Field has
   no photo and no panel, so it sets them to transparent and zero and the same
   markup lays out flat. The negative left margin bleeds the panel past the
   wrap's gutter so the words stay where they were and the shape grows around
   them, rather than the text shifting right by its own padding. */
.hero-panel{background:var(--sk-hero-panel,transparent);
  border-radius:var(--sk-hero-panel-radius,0);
  padding:var(--sk-hero-panel-pad,0);
  margin-left:var(--sk-hero-panel-bleed,0);
  /* The left padding is the bleed, negated. A negative margin moves the
     content box too, so without this the headline slides left by exactly the
     amount the panel grew and ends up off the screen on a phone. Derived
     rather than typed so the two cannot disagree. */
  padding-left:calc(var(--sk-hero-panel-bleed, 0px) * -1);
  max-width:var(--sk-hero-panel-max,none)}
/* Between a phone and a full laptop the wrap stops being 1140px and starts
   filling the window, so the same 34ch of lede occupies a bigger FRACTION of
   the band - about 51% at 1024 against 44% at 1440 - and runs off the left-
   weighted scrim onto the bright side of the truck. Measured at 3.50:1 on an
   iPad in landscape, which is a real device and a failing one. The vertical
   scrim already exists for the phone and is the right shape here too: dark
   where the words are, clearing toward the bottom. */
@media(min-width:901px) and (max-width:1180px){
  .hero::after{background:var(--sk-hero-scrim-narrow,none)}
}
.hero h1{color:var(--sk-hero-ink);max-width:15ch}
/* 38ch, not 52. A hero lede is a subtitle, and at 52ch it ran to 64% of
   the band at 1440 - past where the scrim is still dark, onto the lit
   side of the photograph. The measure is better at 38 anyway; the scrim
   just makes it load-bearing. */
.hero .lede{font-size:clamp(1.03rem,2vw,1.22rem);color:var(--sk-hero-lede);max-width:38ch;margin-bottom:26px}
.cta-row{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:34px}
.credential{border-top:1px solid var(--sk-hero-rule);padding-top:16px;font-size:.855rem;color:var(--sk-hero-cred);max-width:58ch;line-height:1.55}
.credential strong{color:var(--sk-hero-ink);font-weight:600}
.pagehero{background-color:var(--abyss);background-size:cover;
  background-position:var(--photo-pos,center);color:#fff;padding:58px 0 50px}
/* Four overlay stops at 0/40/72/100%, matching the page hero. Every value is a
   token in tokens.css; nothing about the scrim is tuned in this file. */
.pagehero.photo{background-image:linear-gradient(100deg,
    var(--pagehero-o1) 0%,
    var(--pagehero-o2) 40%,
    var(--pagehero-o3) 72%,
    var(--pagehero-o4) 100%),
  var(--photo)}
/* Per-page hero photos. The url() has to live here rather than in a style
   attribute, because a relative url() inside a custom property resolves
   against the stylesheet, not the document — setting --photo inline in the
   HTML resolved to assets/css/assets/img/... and silently rendered no image.
   The framing that goes with each photo is a --ph-*-pos token in tokens.css;
   these rules only wire each one up. Notes on why a photo needs the framing
   it has are with the token, not here. */
.pagehero.ph-irrigation{--photo:var(--ph-irrigation-photo);--photo-pos:var(--ph-irrigation-pos);
  --pagehero-o1:var(--ph-irrigation-o1);
  --pagehero-o2:var(--ph-irrigation-o2);
  --pagehero-o3:var(--ph-irrigation-o3);
  --pagehero-o4:var(--ph-irrigation-o4)}
.pagehero.ph-drainage{--photo:var(--ph-drainage-photo);--photo-pos:var(--ph-drainage-pos)}
.pagehero.ph-harvesting{--photo:var(--ph-harvesting-photo);--photo-pos:var(--ph-harvesting-pos)}
.pagehero.ph-maintenance{--photo:var(--ph-maintenance-photo);--photo-pos:var(--ph-maintenance-pos)}
.pagehero.ph-about{--photo:var(--ph-about-photo);--photo-pos:var(--ph-about-pos);
  --pagehero-o1:var(--ph-about-o1);
  --pagehero-o2:var(--ph-about-o2);
  --pagehero-o3:var(--ph-about-o3);
  --pagehero-o4:var(--ph-about-o4)}
@media(orientation:portrait) and (max-width:560px){
  .pagehero.ph-maintenance{--photo-pos:var(--ph-maintenance-pos-portrait)}
  /* Heroes bright enough that the standard scrim leaves the kicker under AA
     once a portrait crop puts the bright part of the photo behind it. Add a
     selector here to opt another hero in. */
  .pagehero.ph-maintenance,
  .pagehero.ph-harvesting,
  .pagehero.ph-drainage{
    --pagehero-o1:var(--pagehero-o1-portrait);
    --pagehero-o2:var(--pagehero-o2-portrait);
    --pagehero-o3:var(--pagehero-o3-portrait);
    --pagehero-o4:var(--pagehero-o4-portrait)}
}
/* The header is position:sticky, so an anchor jump would otherwise park the
   heading underneath it. Roughly header height plus breathing room - 90 + 18;
   the phone value is lower because the call bar at the bottom pushes content
   up, and because the bar is 78px there rather than 90. */
[id]{scroll-margin-top:108px}
@media(max-width:700px){[id]{scroll-margin-top:78px}}
/* The link that closes a section. Same weight as the link inside a service
   block, on purpose: one offer per section, not a row of demands. */
/* Lists written as page copy. Browser bullets are the one mark on this site
   nobody chose, and they sat next to two that were designed. A short green
   dash reads as a marker rather than as a decoration, and carries no claim -
   which matters, because these lists are faults on irrigation and local
   conditions on the homepage, not things anyone is getting.

   The legal pages keep real bullets: an ordinary list in a privacy policy is
   ordinary on purpose, and a brand-coloured marker there is the site being
   decorative about the part that should read as plain. */
ul.marks{list-style:none;padding-left:0}
ul.marks li{position:relative;padding-left:22px;margin-bottom:.45em}
ul.marks li::before{content:"";position:absolute;left:0;top:.62em;width:10px;
  height:2.5px;background:var(--green-bright);border-radius:2px}
.legal ul.marks{list-style:disc;padding-left:22px}
.legal ul.marks li{padding-left:0}
.legal ul.marks li::before{content:none}

.section-cta{margin:18px 0 0}
/* The 30px of padding under .remote already separates it from the row above,
   so the top margin can be smaller here than the 18px a mid-section link
   wants. No bottom margin: the band pads itself now (see .triage below),
   which is what keeps the spacing right whether or not this link is in the
   copy - it used to carry 26px here and the band went flush the day the link
   came out. */
.triage .section-cta{margin:10px 0 0}
.section-cta a{font-size:.92rem;font-weight:600;text-decoration:none;
  border-bottom:2px solid var(--green-bright);padding-bottom:1px}

.pagehero h1{color:#fff;font-size:clamp(1.85rem,4.4vw,2.7rem)}
.pagehero p{color:#C3DCE7;max-width:60ch;margin-bottom:0}
.kicker{font-size:.85rem;color:var(--green-bright);margin:0 0 10px;font-weight:600}

/* ---------- triage ---------- */
/* padding-bottom, because the band's last child changes with the width and
   none of them carry a bottom margin: .remote above 860px, .paths below it
   once the mobile order kicks in. Both are cards with a border and a
   background, so their edge IS the visual edge, and both were sitting 1px
   from the plan band's colour change - the photo panel's border and the deep
   ocean below it reading as one seam.

   It used to come from .triage .section-cta's 26px bottom margin, which is
   what that rule's comment above still describes. That link is no longer in
   the triage copy, so the band lost its only bottom spacing and nothing
   replaced it - the same shape of bug as the sidenotes, where the gap lived
   on a sibling that happened to be there rather than on the thing that
   actually owns it.

   34px matches the 34px .triage-head holds above the heading. */
.triage{border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);
  background:var(--sk-band-alt);padding-bottom:34px}
.triage-head{padding:34px 0 8px}
.triage-head h2{font-size:1.35rem;margin-bottom:4px}
.triage-head p{color:var(--ink-soft);font-size:.95rem;margin:0}
.paths{display:grid;grid-template-columns:repeat(4,1fr);margin-top:26px;
  gap:var(--sk-card-gap,0);border-top:var(--sk-cell-rule,1px solid var(--rule))}
.path{padding:26px 24px 30px;text-decoration:none;color:var(--ink);
  display:flex;flex-direction:column;transition:background .18s,box-shadow .18s;
  background:var(--sk-card-bg);border:var(--sk-card-border);
  border-right:var(--sk-cell-rule,1px solid var(--rule));
  border-left:var(--sk-card-accent,0);
  border-radius:var(--sk-card-radius);box-shadow:var(--sk-card-shadow)}
.path:first-child{padding-left:var(--sk-cell-inset,0)}
.path:last-child{border-right:var(--sk-card-border);padding-right:var(--sk-cell-inset,0)}
.path:hover{background:var(--tint)}
.path svg{width:30px;height:30px;color:var(--ocean);margin-bottom:14px}
.path .sym{font-family:var(--head);font-weight:600;font-size:1.01rem;line-height:1.33;margin-bottom:9px;letter-spacing:-.012em}
.path .ans{font-size:.885rem;color:var(--ink-soft);line-height:1.5;margin-bottom:14px;flex:1}
.path .go{font-size:.85rem;font-weight:600;color:var(--deep);border-bottom:2px solid var(--green-bright);align-self:flex-start;padding-bottom:1px}
@media(max-width:860px){
  .paths{grid-template-columns:1fr}
  /* Stacked, these are cards rather than cells in a row, so they need
     padding on both sides. Zero was right when they were flush cells in a
     full-width band and wrong once they gained a background and an edge.
     The :first-child and :last-child rules above carry more weight than a
     bare .path, so those two selectors have to be named here as well or
     only the first and last panel keep their padding - which is exactly
     how panels two and three ended up against the edge.

     Both the padding and the divider come from the design: Depth insets its
     text past the green edge and separates cards with a gap, Field keeps
     them flush and separates them with a rule. */
  .path,.path:first-child,.path:last-child{padding:var(--sk-stack-pad)}
  .path{border-right:none;border-bottom:var(--sk-cell-rule,1px solid var(--rule))}
  .path:last-child{border-bottom:none}
}

/* ---------- layout pieces ---------- */
.split{display:grid;grid-template-columns:1.15fr .85fr;gap:60px;align-items:start}
.split.flip{grid-template-columns:.85fr 1.15fr}
@media(max-width:880px){
  .split,.split.flip{grid-template-columns:1fr;gap:34px}
  /* The plan page, stacked: the price goes first, under the header.

     display:contents on the side column so its two children become grid
     items in their own right - that is what lets the price card move up
     while "Why a plan at all" stays where it belongs, at the end. Ordering
     the column as a whole would have dragged the supporting note above
     "What's included", which is not what it supports.

     Only this page has a .pricecard in a .split, so the class is explicit
     rather than a :has() - there is one layout to name and naming it is
     cheaper than a selector every reader has to decode. DOM order is
     unchanged, so the tab order still runs main column, price, note. */
  .split.pricefirst > div:last-child{display:contents}
  .split.pricefirst .pricecard{order:1}
  .split.pricefirst > div:first-child{order:2}
  .split.pricefirst .sidenote{order:3;margin-top:0}

  /* Every other page with a side column ends it with one contextual action -
     "Have us set your controller", "Get a free drainage estimate". Same
     problem and the same mechanism as the plan page above, one step more
     general: the button moves to the top of the band, above the main column,
     and everything else in the side column stays at the end.

     On /watering-rules the button was below two folded tables, three more
     tables and six hundred words, on the page somebody reads to avoid a
     citation. Stacked, the side column is always last, so this was every
     service page.

     Keyed off the button's own class rather than a class per page, because
     there are six of them and one selector is cheaper than six names in six
     files - and build-pages.py derives .aside-cta from the region, so a
     seventh page gets this without anybody remembering to. A browser without
     :has() falls back to the order this replaces.

     DOM order is unchanged, as on the plan page: the tab order still runs main
     column, then the button. The button is a standalone action rather than a
     step in a sequence, and the sticky bar carries the generic one regardless,
     so what the reordering costs is smaller than what it buys. */
  .split:has(.aside-cta) > div:last-child{display:contents}
  .split:has(.aside-cta) > div:last-child > *{order:3}
  .split:has(.aside-cta) > div:last-child > .aside-cta{order:1;margin-top:0}
  .split:has(.aside-cta) > div:first-child{order:2}
  /* The band's 46px of top padding used to sit between the page header and a
     heading. With the action leading the band it sits between the header photo
     and the first thing somebody is meant to tap, so it comes down. Worth
     16px, which is exactly what clears the button of the sticky bar on a
     360x640 screen - the tightest viewport this still has to work on. */
  section.band:has(.aside-cta){padding-top:30px}
}
/* The gap above a side column's action, which used to be an inline 26, 28 or
   30 in six page files. It is one component doing one job; the numbers only
   differed because they were typed on different days. A second, quieter
   action directly under the first keeps its tighter gap. */
.aside-cta{margin-top:30px}
.aside-cta + .aside-cta{margin-top:14px}
.leadin{font-size:1.14rem;color:var(--ink-soft);line-height:1.6}
/* The step below body copy: a qualifier, a caveat, the sentence that adds a
   condition rather than an argument. The site already used this treatment 28
   times as an inline pair; this is the same thing with a name, so the editor
   can produce it without being handed a styling control. 6.17:1 on white and
   5.67:1 on the tinted band. */
.fineprint{font-size:.92rem;color:var(--ink-soft)}
.svc{border-top:1px solid var(--rule);padding:20px 0}
/* An item the plan includes, rather than a service on offer. The tick is the
   same mark the homepage's .incl list uses for the same seven things, so the
   two pages finally look like they are describing one product. The rules
   between items stay - several of these run to three lines and need
   separating - but the first item loses its top rule, because the tick
   already says where the list begins and the heading above it says what
   the list is. */
.svc-inc{border-top:0;padding:13px 0 13px 34px;position:relative}
.svc-inc::before{content:"";position:absolute;left:4px;top:19px;width:14px;
  height:8px;border-left:2.5px solid var(--green-bright);
  border-bottom:2.5px solid var(--green-bright);transform:rotate(-45deg)}
.svc-inc + .svc-inc{border-top:1px solid var(--rule)}
.svc h3{margin-bottom:5px}
.svc p{font-size:.94rem;color:var(--ink-soft);margin:0 0 8px}
.svc a{font-size:.88rem;font-weight:600;text-decoration:none;border-bottom:2px solid var(--green-bright)}

/* ---------- panel: a service section, folded ---------- */
/* One component for the service pages. It reads the card tokens, so depth
   gets a solid card with a green edge and field gets a hairline square one,
   and the long copy sits inside a <details> rather than down the page. The
   text is still in the HTML either way - this hides it from a reader who has
   decided, not from a search engine. */
.panel{background:var(--sk-card-bg);border:var(--sk-card-border);border-left:var(--sk-card-accent,3px solid var(--green));border-radius:var(--sk-card-radius);box-shadow:var(--sk-card-shadow);padding:22px 26px;margin:0 0 var(--sk-card-gap,16px)}
.panel h2,.panel h3{font-size:1.16rem;margin:0 0 7px}
/* The anchor target is the heading inside the card, so it carries the
   card's own top padding on top of the sticky-header offset. Without
   this an #anchor jump clips the top edge of the panel it landed on. */
.panel h2[id],.panel h3[id]{scroll-margin-top:132px}
.panel .panel-sum{font-size:.95rem;color:var(--ink-soft);margin:0}
.panel-more{margin-top:12px}
.panel-more summary{font-family:var(--head);font-weight:600;font-size:.9rem;color:var(--deep);cursor:pointer;list-style:none;display:inline-flex;gap:8px;align-items:baseline}
.panel-more summary::-webkit-details-marker{display:none}
.panel-more summary::after{content:"+";font-size:1.15rem;line-height:1}
.panel-more[open] summary::after{content:"\2013"}
.panel-more summary:hover{text-decoration:underline}
.panel-more p{font-size:.95rem;color:var(--ink-soft);margin:12px 0 0}
.panel-more ul.marks{margin:10px 0 0}
.panel-more .rev{margin:16px 0 0}
.panel .section-cta{margin:14px 0 0}
.sidenote{background:var(--sk-card-bg);border:var(--sk-card-border);border-left:var(--sk-card-accent,3px solid var(--green));border-radius:var(--sk-card-radius);box-shadow:var(--sk-card-shadow);padding:24px 26px}
/* Stacked notes space themselves. The card carries no margin of its own, so
   until this existed the gap between two of them was whatever inline
   margin-top the author had remembered - and on /irrigation two of the three
   had none, so the blue plan card and the green service-area card shared an
   edge and read as one two-tone card. Spacing between siblings is not
   content, so it does not belong in the page JSON where an editor adding a
   fourth note would have to know to set a hidden field. */
.sidenote + .sidenote{margin-top:22px}
.sidenote.blue{border-left-color:var(--ocean)}
/* h3 by document level, h4 by appearance - the size and tracking are
   restated so relevelling changed nothing anyone can see. */
.sidenote h3,.step h3,.answer h3{font-size:1.02rem;letter-spacing:0}
.sidenote h3{margin-bottom:8px}
.sidenote p{font-size:.92rem;color:var(--ink-soft);margin-bottom:0}
.sidenote p + p{margin-top:10px}
figure.photo{margin:0 0 24px}
figure.photo img{width:100%;height:auto;border-radius:var(--sk-card-radius);background:var(--tint)}
figure.photo figcaption{font-size:.82rem;color:var(--ink-faint);margin-top:8px}

.steps{counter-reset:s;display:grid;grid-template-columns:repeat(3,1fr);margin-top:30px}
.steps{gap:var(--sk-card-gap,0)}
.step{counter-increment:s;padding:var(--sk-step-pad,0 30px);
  border-left:var(--sk-cell-rule,1px solid var(--rule));
  background:var(--sk-step-bg,transparent);
  border-top:var(--sk-step-accent,0);
  border-radius:var(--sk-card-radius)}
.step:first-child{padding-left:var(--sk-cell-inset,0);
  border-left:var(--sk-step-side,none)}
.step:last-child{padding-right:var(--sk-cell-inset,0)}
.step::before{content:counter(s);font-family:var(--head);font-weight:700;font-size:2.4rem;color:var(--green-bright);line-height:1;display:block;margin-bottom:12px}
.step h3{margin-bottom:6px}
.step p{font-size:.93rem;color:var(--ink-soft);margin:0}
@media(max-width:800px){
  .steps{grid-template-columns:1fr}
  /* Same trap as the entry panels: a bare .step here loses to the
     :first-child rule above it, so step one kept its padding and the other
     two lost theirs. All three selectors have to be named.

     Padding, the divider and the spacing all come from the design. Depth
     stacks them as cards - inset text, a green edge on each, spaced by the
     grid gap. Field stacks them as cells - flush text, a hairline between,
     and no rule above the first one because nothing sits above it. */
  .step,.step:first-child,.step:last-child{padding:var(--sk-stack-pad)}
  .step{border-left:none;border-top:var(--sk-stack-divider);
    margin-top:var(--sk-stack-gap)}
  .step:first-child{border-top:var(--sk-stack-first);margin-top:0}
}

/* ---------- plan ---------- */
/* The plan band. Depth keeps the deep-ocean gradient; Field turns it white
   with an ink rule, so every colour in it has to come from the design. */
.planband{background-color:var(--sk-planband-flat);
  background-image:var(--sk-planband-image,none);color:var(--sk-panel-soft);
  border-top:var(--sk-planband-edge,0);border-bottom:var(--sk-planband-edge,0)}
.planband h2{color:var(--sk-panel-ink)}
.plangrid{display:grid;grid-template-columns:.78fr 1.22fr;gap:56px;align-items:center}
@media(max-width:880px){.plangrid{grid-template-columns:1fr;gap:30px}}
.price{font-family:var(--head);font-weight:700;font-size:4rem;line-height:.9;color:var(--sk-panel-ink);letter-spacing:-.035em}
.price small{font-size:1.05rem;font-weight:600;letter-spacing:0;color:var(--sk-panel-soft)}
.plan-alt{font-size:.9rem;color:var(--sk-panel-soft);margin-top:10px}
.incl{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:1fr 1fr;gap:14px 30px}
@media(max-width:620px){.incl{grid-template-columns:1fr}}
.incl li{padding-left:26px;position:relative;font-size:.95rem;color:var(--sk-panel-soft);line-height:1.45}
.incl li::before{content:"";position:absolute;left:0;top:7px;width:13px;height:8px;border-left:2.5px solid var(--green-bright);border-bottom:2.5px solid var(--green-bright);transform:rotate(-45deg)}
.incl li b{color:var(--sk-panel-ink);font-weight:600;display:block}
.pricecard{background:var(--sk-panel-bg);color:var(--sk-panel-ink);border:var(--sk-panel-border);padding:32px 28px;border-radius:var(--sk-panel-radius)}
.pricecard p,.pricecard small,.pricecard .plan-alt{color:var(--sk-panel-soft)}
.pricecard .fine{color:#7FA9BD;font-size:.78rem;margin:14px 0 0;line-height:1.5}
/* The scope line above the button is a separate fact from the price above
   it, and with no margin the two read as one run-on paragraph. */
.pricecard .fineprint{margin-top:10px}

/* ---------- proof ---------- */
.reviews{display:grid;grid-template-columns:1fr 1fr;gap:34px 50px;margin-top:8px}
@media(max-width:800px){.reviews{grid-template-columns:1fr}}
.rev{border-left:3px solid var(--green-bright);padding-left:20px;
  background:var(--sk-quote-bg,transparent);
  padding:var(--sk-quote-pad,0 0 0 20px);
  border-radius:var(--sk-card-radius);box-shadow:var(--sk-quote-shadow,none)}
.rev p{font-size:.985rem;margin-bottom:9px}
.rev cite{font-style:normal;font-size:.85rem;color:var(--ink-faint)}
.rev cite b{color:var(--ink);font-weight:600}

/* ---------- jobber ---------- */
/* Centring only. Jobber's embed script injects its own iframe and sizes it
   from their stylesheet, which we do not control and cannot test against
   locally, so nothing here sets a width, a height or a display on it -- a
   wrong guess would be on the booking form, which is the last place to
   experiment. An iframe is inline-level by default, so text-align centres it;
   margin-inline covers the case where their CSS makes it a block. Neither
   changes its size.

   The descendant div matters and was missed the first time: Jobber nests the
   iframe inside a div they size, so a rule aimed at the iframe never reached
   the element that was actually offset, and the form stayed hard left. Only
   markup on this page is in scope - the form itself is inside the iframe, a
   separate document our stylesheet cannot touch - so this is their one or two
   wrapper divs and nothing else. */
/* The booking form embed, wearing the same card as everything else.
   Only the frame is ours: the form inside is a cross-origin iframe and
   nothing here reaches it. What that leaves is the four things the card
   is made of - surface, edge, radius, shadow - all read from --sk-card-*,
   so it follows the design like a .sidenote rather than being a second
   idea about what a panel looks like.

   width:100% is the fix that came first, and it was missing for a long
   time. An iframe with no width falls back to the HTML default of 300px,
   which is what Jobber's script creates, so the form rendered 300px wide
   inside a 596px column and looked like an embed with padding around it.
   It was not padding; it was an unstyled iframe at its default size.
   Their stylesheet loads before ours, so a plain rule wins it.

   border:0 on the iframe drops the browser's default 2px inset frame,
   which is the grey line that used to sit inside our card. display:block
   drops the few pixels of descender space an inline iframe leaves beneath
   itself - invisible before, an uneven bottom edge once the card has a
   visible boundary. overflow:hidden is what makes the radius actually
   clip the iframe's corners.

   No padding: the form brings generous spacing of its own, and a second
   ring of it would squeeze the fields into a column.

   Height is still left alone. Their script measures the form and resizes
   the iframe across its three steps, and a height here would fight it. */
.jobber-embed{background:var(--sk-card-bg);border:var(--sk-card-border);border-left:var(--sk-card-accent,3px solid var(--green));border-radius:var(--sk-card-radius);box-shadow:var(--sk-card-shadow);overflow:hidden}
.jobber-embed iframe{display:block;width:100%;max-width:100%;border:0}

.jobber-slot{border:1px dashed var(--ocean);background:var(--sk-band-alt);border-radius:var(--sk-card-radius);padding:30px 26px}
.jobber-slot .tag{font-family:var(--head);font-size:.74rem;font-weight:700;color:var(--ocean);letter-spacing:.06em;margin-bottom:10px}
.jobber-slot p{font-size:.9rem;color:var(--ink-soft);margin-bottom:16px}
code{font-size:.82rem;background:var(--tint);border:1px solid var(--rule);padding:2px 5px;border-radius:3px;word-break:break-all}

/* ---------- content bits ---------- */
table.rules{width:100%;border-collapse:collapse;margin:18px 0 8px;font-size:.94rem}
table.rules th,table.rules td{text-align:left;padding:11px 14px;border-bottom:1px solid var(--rule)}
table.rules th{font-family:var(--head);font-weight:600;font-size:.85rem;background:var(--tint)}
/* Four columns did not fit a phone: at 360px this table pushed the document
   60px past the screen edge, on the one page where somebody is reading a
   table to avoid a citation. The fold in build-watering.py takes the first
   column away; this takes the rest of the width back. */
@media(max-width:560px){
  table.rules{font-size:.88rem}
  table.rules th,table.rules td{padding:9px 9px}
}

/* One fold per side of the boundary. Card-shaped rather than the hairline
   .faq fold, because each one holds a table rather than a paragraph. */
details.sched{border:1px solid var(--rule);border-radius:var(--sk-card-radius);margin:14px 0;overflow:hidden}
details.sched > summary{font-family:var(--head);font-weight:600;font-size:1rem;cursor:pointer;list-style:none;display:flex;justify-content:space-between;gap:16px;align-items:center;padding:14px 16px}
details.sched > summary::-webkit-details-marker{display:none}
details.sched > summary::after{content:"+";color:var(--ocean);font-size:1.3rem;line-height:1}
details.sched[open] > summary{border-bottom:1px solid var(--rule)}
details.sched[open] > summary::after{content:"\2013"}
details.sched table.rules{margin:0}
details.sched table.rules tr:last-child td{border-bottom:0}
.statusbox{border:2px solid var(--ocean);border-radius:var(--sk-card-radius);padding:22px 24px;background:var(--sk-card-bg);box-shadow:var(--sk-card-shadow);margin-bottom:30px}
.statusbox .when{font-size:.8rem;color:var(--ink-faint);margin-bottom:8px}

/* The county's own schedule graphic, in the column beside the rules, and the
   cue on the one outbound link. Card tokens like every other panel here, so it
   follows the design instead of being a white box in a dark band. */
.sidefig{background:var(--sk-card-bg);border:var(--sk-card-border);border-radius:var(--sk-card-radius);box-shadow:var(--sk-card-shadow);padding:14px;margin:0 0 26px}
.sidefig img{display:block;width:100%;height:auto;border-radius:calc(var(--sk-card-radius) / 2)}
.sidefig figcaption{font-size:.84rem;color:var(--ink-soft);margin-top:10px}
.newtab{font-size:.82rem;color:var(--ink-faint)}
.statusbox p:last-child{margin-bottom:0}
/* Only the live render can know the page has gone stale; the baked-in
   copy has no idea how old it is. */
.statusbox .stale-flag{background:#FFF8E1;border-left:3px solid #E0A800;
  padding:10px 14px;font-size:.9rem;color:#5B4A12;margin:0 0 14px}
.statusbox .stale-flag strong{color:#3F3308}
details.faq{border-bottom:1px solid var(--rule);padding:16px 0}
details.faq summary{font-family:var(--head);font-weight:600;font-size:1rem;cursor:pointer;list-style:none;display:flex;justify-content:space-between;gap:16px;align-items:baseline}
details.faq summary::-webkit-details-marker{display:none}
details.faq summary::after{content:"+";color:var(--ocean);font-size:1.3rem;line-height:1}
details.faq[open] summary::after{content:"\2013"}
details.faq p{font-size:.95rem;color:var(--ink-soft);margin:12px 0 0}
.legal h2{font-size:1.25rem;margin-top:38px}
.legal h3{font-size:1.02rem;margin-top:24px}
.legal p,.legal li{font-size:.95rem;color:var(--ink-soft)}
.legal ul{max-width:68ch}
/* There used to be a .todo in this same amber, for notes to ourselves, and it
   shipped visibly at the top of /terms/ and /privacy/. A note to yourself on a
   page a customer reads is not a reminder, it is a disclosure - it tells a
   homeowner the terms they are agreeing to are unfinished. The class is gone;
   the list lives in src/data/todo.json, is editable in the CMS under
   Outstanding items, renders to no page, and is printed by every build.
   tools/check-todo.py refuses the class anywhere in the output.
   .notice is the amber box that is written for a customer and ships. */
.notice{background:#FFF8E1;border-left:3px solid #E0A800;padding:14px 18px;font-size:.88rem;color:#5B4A12;margin:16px 0}
.notice b{color:#3F3308}

/* ---------- remote-diagnosis row (homepage triage) ---------- */
/* The catch-all row under the four symptom cards. It reads as a card too,
   from the same tokens, so it is plainly one of the ways in rather than a
   footnote - and so it does not run together with the pricing link below it,
   which is a different kind of thing entirely. The 18px is flat rather than
   --sk-card-gap because the gap is separating two KINDS of block here, which
   both designs want, not two cells in one row, which only Depth does. */
.remote{display:flex;gap:20px;align-items:center;margin-top:18px;
  padding:22px 24px;text-decoration:none;color:var(--ink);
  background:var(--sk-card-bg);border:var(--sk-card-border);
  border-left:var(--sk-card-accent,0);
  border-radius:var(--sk-card-radius);box-shadow:var(--sk-card-shadow)}
.remote:hover{background:var(--tint)}
.remote svg{width:30px;height:30px;color:var(--green);flex:0 0 auto}
.rt{flex:1;font-size:.9rem;color:var(--ink-soft);line-height:1.5}
.rt b{display:block;font-family:var(--head);font-weight:600;font-size:1.01rem;color:var(--ink);letter-spacing:-.012em;margin-bottom:4px}
.remote .go{font-size:.85rem;font-weight:600;color:var(--deep);border-bottom:2px solid var(--green-bright);padding-bottom:1px;flex:0 0 auto}
/* Below 860px the four cards above this one stack, and each puts its glyph
   above its text. This row kept its desktop shape - glyph on the left, link
   pinned right - so in a stack of five cards it was the one laid out
   differently. It takes the same column now: glyph, heading, body, link,
   with the link only as wide as its own text rather than the full card. */
@media(max-width:860px){
  .remote{flex-direction:column;align-items:flex-start;gap:0}
  .remote svg{margin-bottom:14px}
  .remote .rt{flex:none}
  .remote .go{align-self:flex-start;margin-top:14px}
  /* And it leads, once the row has become a stack.

     Below 860px the desktop layout does not exist any more: the four cards
     are already one per row, so the photo card is simply the fifth in a
     column, and fifth reads as "if none of the others fit" - which is what
     its heading used to say out loud. It is the only free route in the set
     and the only one needing no appointment, so it is the one worth reaching
     first on a phone. Above 860px nothing moves: four across, this full
     width beneath them.

     It goes above the section heading, not between it and the cards. "Do any
     of these sound familiar? Pick one to get started" introduces a list of
     symptoms; with the photo card under it that sentence described the wrong
     thing. Above the heading the card reads as its own offer and the heading
     still introduces the four cards that follow it.

     Flex order rather than separate markup, so there is one card and one
     link. The tab order still runs heading, four symptoms, photo card, which
     is a sensible sequence read in either position. */
  .triage .wrap{display:flex;flex-direction:column}
  .remote{order:1;margin-top:28px}
  .triage-head{order:2}
  .paths{order:3}
  .triage .section-cta{order:4}
}

/* ---------- question / answer pairs (pricing) ---------- */
.qa{border-top:1px solid var(--rule);padding:20px 0}
.qa h3{margin-bottom:6px}
.qa p{font-size:.94rem;color:var(--ink-soft);margin:0}
.answer{background:var(--sk-card-bg);border:var(--sk-card-border);border-left:3px solid var(--ocean);border-radius:var(--sk-card-radius);box-shadow:var(--sk-card-shadow);padding:24px 26px;margin-top:26px}
.answer h3{margin-bottom:10px}
.answer p{font-size:.94rem;color:var(--ink-soft);margin-bottom:0}

/* ---------- blog ---------- */
.post .wrap{padding-top:52px;padding-bottom:10px}
.post-meta{font-size:.84rem;color:var(--ink-faint);margin-bottom:10px}
.post-meta a{color:var(--deep)}
.post h1{font-size:clamp(1.75rem,4vw,2.5rem);margin-bottom:18px}
.post-hero{width:100%;height:auto;border-radius:var(--sk-card-radius);margin:6px 0 30px}
.post h2{font-size:1.4rem;margin:38px 0 .5em}
.post h3{font-size:1.16rem;margin:32px 0 .5em}
.post h4{margin:26px 0 .5em}
.post li{margin-bottom:.5em}
.post ul,.post ol{max-width:68ch;padding-left:1.25em}
.post img{border-radius:5px;margin:22px 0}
.post blockquote{border-left:3px solid var(--green-bright);padding-left:20px;margin:26px 0;color:var(--ink-soft)}

.post-cta{background:var(--sk-card-bg);border:var(--sk-card-border);border-left:3px solid var(--ocean);border-radius:var(--sk-card-radius);box-shadow:var(--sk-card-shadow);padding:26px 28px;margin:44px 0 0}
.post-cta h3{margin:0 0 8px;font-size:1.1rem}
.post-cta p{font-size:.95rem;color:var(--ink-soft)}
.post-cta-row{display:flex;gap:12px;flex-wrap:wrap;margin:0}

.post-nav{display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap;
  border-top:1px solid var(--rule);margin-top:44px;padding:22px 0 10px;font-size:.92rem}
.post-nav a{display:block;max-width:34ch;text-decoration:none;color:var(--deep);font-weight:600}
.post-nav a:hover{text-decoration:underline}
.post-nav .prev::before{content:"\2190  ";color:var(--ink-faint)}
.post-nav .next{text-align:right;margin-left:auto}
.post-nav .next::after{content:"  \2192";color:var(--ink-faint)}
@media(max-width:620px){.post-nav .next{text-align:left;margin-left:0}}

.post-list{list-style:none;margin:0;padding:0}
.post-card{display:grid;grid-template-columns:150px 1fr;gap:22px;align-items:start;
  border-top:1px solid var(--rule);padding:24px 0}
.post-card:first-child{border-top:0;padding-top:0}
.post-card-img{display:block;border-radius:var(--sk-card-radius);overflow:hidden;background:var(--tint)}
.post-card-img img{width:150px;height:100px;object-fit:cover;display:block}
.post-card-date{font-size:.8rem;color:var(--ink-faint);margin:0 0 5px}
.post-card h3{margin:0 0 6px;font-size:1.08rem}
.post-card h3 a{color:var(--ink);text-decoration:none}
.post-card h3 a:hover{color:var(--deep);text-decoration:underline}
.post-card p{font-size:.92rem;color:var(--ink-soft);margin:0;max-width:none}
@media(max-width:620px){
  .post-card{grid-template-columns:1fr;gap:12px}
  .post-card-img img{width:100%;height:170px}
}

/* ---------- footer ---------- */
footer.site{background:var(--sk-footer-bg);color:var(--sk-footer-ink);padding:58px 0 26px;font-size:.9rem}
footer.site a{color:var(--sk-footer-ink);text-decoration:none}
footer.site a:hover{color:#fff;text-decoration:underline}
.fgrid{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;gap:38px}
@media(max-width:820px){.fgrid{grid-template-columns:1fr 1fr;gap:30px}}
/* The footer's column headings are the top-level headings of the footer
   landmark, so they are h2. font-weight is restated because the shared
   h4 rule used to supply it. */
footer.site h2{color:#fff;font-size:.82rem;font-weight:600;letter-spacing:.05em;margin-bottom:14px}
footer.site ul{list-style:none;margin:0;padding:0}
footer.site li{margin-bottom:9px}
footer.site .flogo{height:74px;width:auto;margin-bottom:14px;background:#fff;padding:7px;border-radius:5px}
/* The typeset alternative to the mark, shown only where the mark is not.
   Not a logo and not trying to be one: the business name in the heading
   face, no mark beside it, no recolouring of anything. */
footer.site .fname{display:none;font-family:"Montserrat",system-ui,sans-serif;
  font-size:1.16rem;font-weight:700;color:#fff;margin:0 0 12px;letter-spacing:.01em}
/* Four stacked columns made the footer 1074px tall at 360px - a screenful
   and a half of link list after someone has already reached the end of the
   page. Two columns takes about a fifth off it. Nothing is hidden: the
   Contact block is the NAP Google reads against the Business Profile, and a
   footer link behind display:none is a link Google discounts, so the answer
   here is layout rather than removal. Sits below .flogo because it lowers
   the logo, and that rule would otherwise win on source order. */
@media(max-width:520px){
  .fgrid{grid-template-columns:1fr 1fr;gap:26px 18px}
  /* The brand and the contact block span, for opposite reasons: the tagline
     is set to 34ch and wraps to six lines in half a phone, and the contact
     list is worth running two-up. Services and Company are short enough to
     sit beside each other, which is where the height actually comes off. */
  .fgrid > :first-child, .fgrid > :last-child{grid-column:1 / -1}
  .fgrid > :last-child ul{display:grid;grid-template-columns:1fr 1fr;
    gap:0 18px;align-items:start}
  /* The one item in that list that must not be cut in half. */
  .fgrid .faddr{grid-column:1 / -1}
  footer.site li{margin-bottom:11px}
  /* On a phone the header is sticky, so reaching the footer puts the mark
     on screen twice. The name carries the block instead - the tagline and
     the licence line underneath it need something to belong to, and an
     unsigned paragraph in a dark band reads as orphaned. */
  footer.site .flogo{display:none}
  footer.site .fname{display:block}
}
.fbottom{border-top:1px solid rgba(255,255,255,.14);margin-top:42px;padding-top:20px;font-size:.8rem;color:var(--sk-footer-faint);display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap}

.calltrap{position:fixed;left:0;right:0;bottom:0;z-index:70;display:none;background:var(--ct-bar);padding:10px 12px calc(10px + env(safe-area-inset-bottom,0px));gap:10px}
/* Three buttons at 25/25/50. Grow factors rather than percentage widths,
   so the two 10px gaps come off the total before the split and nothing
   overflows at 360px. Call and Text are the same white: they are the same
   kind of action, reaching a person now, and Request is the one that is
   different - a form, and the widest target. */
.calltrap a{flex:1;text-align:center;font-family:var(--head);font-weight:600;font-size:.92rem;padding:12px 8px;border-radius:var(--sk-btn-radius);text-decoration:none}
.calltrap .c1{background:var(--ct-call-bg);color:var(--ct-call-ink)}
.calltrap .c3{background:var(--ct-text-bg);color:var(--ct-text-ink)}
.calltrap .c2{background:var(--ct-request-bg);color:var(--ct-request-ink);flex:2}
@media(max-width:700px){.calltrap{display:flex}body{padding-bottom:70px}}
/* ---------- tones ----------
   How serious something is, in four steps. A .tone-* class only declares
   three local variables; it paints nothing itself. Components read them, so
   a tone can be put on a band, a box or a chip and they all agree - the
   announcement strip, the water shortage phase, and whatever comes next.
   The twelve values are in build-theme.py and every one of them is measured
   there, ink against its own background and edge against the page.

   The tone is never the message. Anything wearing one still says in words
   what it means, because roughly one man in twelve cannot separate the red
   from the orange (WCAG 1.4.1 Use of Color). */
.tone-info{--tone-bg:var(--sk-tone-info-bg);--tone-ink:var(--sk-tone-info-ink);
  --tone-edge:var(--sk-tone-info-edge)}
.tone-caution{--tone-bg:var(--sk-tone-caution-bg);--tone-ink:var(--sk-tone-caution-ink);
  --tone-edge:var(--sk-tone-caution-edge)}
.tone-warning{--tone-bg:var(--sk-tone-warning-bg);--tone-ink:var(--sk-tone-warning-ink);
  --tone-edge:var(--sk-tone-warning-edge)}
.tone-critical{--tone-bg:var(--sk-tone-critical-bg);--tone-ink:var(--sk-tone-critical-ink);
  --tone-edge:var(--sk-tone-critical-edge)}

/* The announcement strip, between the utility bar and the header. Off unless
   there is news, so it keeps its force. */
.newsbar{background:var(--tone-bg);color:var(--tone-ink);
  border-bottom:3px solid var(--tone-edge);font-size:.92rem}
.newsbar p{margin:0;padding:9px 0}
.newsbar a{color:inherit;font-weight:700;text-decoration:underline;
  text-underline-offset:2px}
@media(max-width:700px){.newsbar{font-size:.87rem}.newsbar p{padding:8px 0}}

/* The water shortage phase, wherever it appears. A chip names the phase in
   words and takes its colour from the tone around it; the panel is the same
   thing given room to also cite the rule it was declared under. */
.phase-chip{display:inline-block;background:var(--tone-bg);color:var(--tone-ink);
  border:1px solid var(--tone-edge);border-radius:999px;padding:4px 13px;
  font-size:.84rem;font-weight:600;line-height:1.35;text-decoration:none}
a.phase-chip:hover{border-color:var(--tone-ink)}
.chiprow{margin:0 0 12px}
.phasepanel{background:var(--tone-bg);color:var(--tone-ink);
  border:1px solid var(--tone-edge);border-left:5px solid var(--tone-edge);
  border-radius:var(--sk-card-radius);padding:18px 22px;margin-bottom:22px}
.phasepanel p{margin:0 0 8px}
.phasepanel p:last-child{margin-bottom:0}
.phasepanel .phase-now{font-size:1.06rem}
.phasepanel .phase-rule{font-size:.86rem}
.phasepanel a{color:inherit;text-decoration:underline;text-underline-offset:2px}

/* The homepage box wears a tone on its edge only: it sits on a white band
   beside other cards, and a tinted card there would read as a different kind
   of component rather than as the same card carrying news. The phase is
   spelled out on the chip inside it either way. */
.statusbox.tone-info, .statusbox.tone-caution,
.statusbox.tone-warning, .statusbox.tone-critical{border-color:var(--tone-edge)}

/* The homepage's booking section goes when the sticky bar arrives, at the
   same breakpoint, because the two are substitutes: Call, Text and Request
   service are all in the bar, and the embedded Jobber form is a long scroll
   to put in front of someone who already has a Request service button fixed
   to the bottom of the screen. The bar sends them to /request/, which is the
   same form on a page that is about nothing else.

   Only the homepage. #book is on no other page, and /request/ and /enroll/
   are the form, so hiding it there would leave a page with nothing on it. */
@media(max-width:700px){#book{display:none}}

/* The hero stacks before the rest of the page does: two columns of this
   weight stop working well before 700px.
   
   Below that width the hero also sheds two things, to get the four entry
   panels closer to the top of the page. Measured: they started 350-610px
   below the fold on five common phones, which is a whole extra screen.

   The button goes because the sticky call bar already carries Request
   service, fixed to the bottom of every screen from the first pixel - the
   hero button is the duplicate, not the bar. The licence line goes because
   the badge row further down and the footer both carry the same claims,
   licence number included. Neither is hidden on desktop, where there is
   room for both. */
@media(max-width:900px){
  /* 28/32 rather than 40/46. The band is 485px of a 664px first screen on an
     iPhone once the header is counted, and what sits under it is the free
     photo route - the thing worth reaching without scrolling. Every pixel
     taken here is padding, not words: the headline, the lede and the phase
     chip are all untouched. */
  .hero .wrap{padding-top:28px;padding-bottom:32px}
  /* The text spans the width here rather than sitting in the left half, so
     a scrim weighted to the left has nothing to fade into. It goes vertical
     instead. The framing is its own field as well: a phone crops a tall
     narrow slice out of a wide photograph, so the laptop framing lands
     somewhere else entirely. */
  /* Edge to edge on a phone: a rounded card inset from both gutters wastes
     width the headline needs, and the photograph above and below it is doing
     the work the corners would. Re-pointing the bleed at the wrap's own 22px
     gutter moves the margin and the padding together, so the words stay put
     and only the panel grows. */
  .hero-panel{--sk-hero-panel-bleed:-22px;
    /* Re-pointed the same way the bleed is, so the panel keeps reading from
       its token rather than growing a literal. The left value is derived
       from the bleed below and is ignored here. */
    --sk-hero-panel-pad:26px 30px 28px 0;
    margin-right:var(--sk-hero-panel-bleed);
    max-width:none;border-radius:0}
  /* The phone crop, and the reason it needs a zoom at all. This photograph is
     2.56:1 and the band here is about 1.28:1, so `cover` fits it by height and
     crops only the sides - no vertical overflow, which means the vertical half
     of the framing field moves nothing. Zooming past cover makes the room that
     number then moves within, and at 138% the file is still being scaled down
     from 1800px, so nothing softens. 100% is plain cover, which is the default
     for any photo that does not need this. */
  .hero::before{background-position:var(--ph-home-pos-narrow,var(--ph-home-pos,center));
    background-size:auto var(--ph-home-zoom-narrow,100%)}
  /* Its own scrim below 900px, not the one 901-1180 uses. The wash only has
     to carry the words - the button is back at this width, but it sits inside
     .hero-panel, so what it has to clear under 1.4.11 is that panel and not
     the photograph, and build-theme.py measures that pair over white at
     4.32:1. So the picture keeps its extra brightness here. Falls back to the
     narrow scrim if a design does not define this one. */
  .hero::after{background:var(--sk-hero-scrim-phone,var(--sk-hero-scrim-narrow,none))}
  .hero .lede{max-width:none;margin-bottom:18px}
  /* The button shows at this width again. It was hidden here for a while on
     the argument that the sticky bar carries the same action - but the sticky
     bar is furniture at the other end of the screen, and it stops at 700px,
     so between 701 and 900 the hero had no way to act on what it had just
     said. Brian's call.
     It sits where it sits in the markup, under the lede: the chip is the only
     thing flex order moves, so reading order and tab order still agree.
     Roughly twice the chip's height - the chip is 26px here and this lands at
     52 - because one of them is a label and the other is the thing to press,
     and at the chip's size it would have read as a second label. */
  /* The row's own 34px bottom margin was spacing it from the next band;
     inside the panel it lands on top of the panel's 28px of padding. */
  .hero .cta-row{margin-bottom:0}
  .hero .cta-row .btn{padding:12px 24px;font-size:.95rem}
  /* The chip goes to the top of the panel on a phone, and gets quieter.
     Below the lede it landed across the truck, which is the one thing this
     photograph is of; above the headline it sits on sky. Flex order rather
     than different markup, so the reading order stays headline, lede, chip -
     and nothing else in the panel is focusable at this width, so the visual
     order and the tab order cannot disagree. */
  .hero .hero-panel{display:flex;flex-direction:column}
  .hero .chiprow{order:-1;margin:0 0 15px}
  /* 4px rather than 3 in the padding: at 3 the chip measured exactly
     24px tall, which is the floor for 2.5.8 Target Size and no room
     for a rounding error. */
  .hero .phase-chip{font-size:.76rem;padding:4px 11px;font-weight:600}
  .hero .credential{display:none}
  /* No bottom margin here: the licence line it was spacing away from is
     hidden at this width, and those 26px are the difference between the
     first panel clearing the fold on a Pixel and not. */
  .hero .section-cta{margin:4px 0 0}
  .triage-head{padding-top:22px}
  .paths{margin-top:18px}
  /* 74px of band padding is a desktop rhythm. On a phone it is most of a
     thumb-scroll of nothing between one band's last line and the next
     band's heading, twice over per band. */
  section.band{padding:46px 0}
  section.band.tight{padding:34px 0}
  .remote{padding:18px 20px}
  /* Narrower than the 34px above, for the same reason section.band drops
     from 74 to 46 in this block: a desktop rhythm is a lot of empty thumb
     travel on a phone. Note this block starts at 900px while the mobile
     re-order starts at 860, so between the two .remote is still the last
     child and gets this value - which is fine, it is a gap either way. */
  .triage{padding-bottom:28px}
}

/* ---------------------------------------------------------------- designs
   Last in the file on purpose. Every rule above was written when each band
   was light, so they set ink colours at the same specificity these need to
   beat; source order is what settles it. Nothing here is design-specific -
   it reads the tokens, and the two designs put different values in them. */
.feature h2,.feature h3,.feature .step::before{color:var(--sk-feature-ink)}
.feature p,.feature li,.feature .step p,.feature .svc p{color:var(--sk-feature-soft)}
.feature .fineprint,.feature .lede,.feature .kicker{color:var(--sk-feature-faint)}
.feature .leadin{color:var(--sk-feature-soft)}
/* A link written into copy on a dark band.

   The global rule is a{color:var(--deep)}, which is the link colour for a
   light band - and --deep IS --sk-feature-bg in Depth, both #005A8C. So a
   link in this band rendered at 1.00:1 against the band it sat on: present,
   underlined, clickable, and the exact colour of what was behind it. It
   shipped that way the first time someone wrote one, because until then
   every link on the site sat on white.

   Inheriting the band's own ink is the fix that needs no new measured pair -
   the colour it lands on is the one build-theme.py already checks for that
   band's body copy. The underline is what tells you it is a link, so the
   link is not signalled by colour alone (1.4.1), which matters more here
   than on white because there is no second colour to give it.

   :not([class]) is the whole selector for "written into copy": the copy
   allow-list in build-pages.py admits <a href="..."> and nothing else, so a
   link from an editor can never carry a class, and every button, chip and
   CTA in these bands does. They bring their own fill and their own measured
   ink and must not be touched.

   Four surfaces, which is every one an editor's words can land on that is
   not white: the feature band, the plan band, the hero panel and the price
   card. Page headers are dark too and are not here, because build-heroes.py
   escapes their copy with no allow-list at all - a link cannot be written
   into one. If a fifth dark surface ever takes page copy, it belongs in this
   list, and tools/audit-contrast.py is what will say so: it measured this
   bug at 1.00:1 and named the words. */
.feature a:not([class]),
.planband a:not([class]),
.hero-panel a:not([class]),
.pricecard a:not([class]){color:inherit;text-decoration:underline;
  text-decoration-thickness:1px;text-underline-offset:3px}
/* The green underline on this link and the hairline above the licence line
   end up about ten pixels apart otherwise, which reads as a mistake.
   Bounded by min-width rather than left unqualified: this block is the last
   in the file, so an unqualified rule here beats the max-width query above
   it on source order and the spacing comes back on a phone, where there is
   no licence line to space away from and 26px costs the fold. */
@media(min-width:901px){.hero .section-cta{margin:20px 0 26px}}
/* The button closing the process section had 0px above it and 108px below:
   flush against the step cards, then the .cta-row's own 34px plus the band's
   padding. The band's padding is the band's rhythm and is left alone; the
   stray bottom margin goes and the space moves above the button, where it
   was missing. */
.feature .cta-row{margin:30px 0 0}
.hero .section-cta a{color:var(--sk-hero-ink)}
/* The hero's Request service button. .btn-light reads --sk-cta-* four lines
   into this file, and that pair also paints the header button and the two on
   the plan band - all of which sit on dark surfaces and stay lawn green. This
   band is pale in Depth, where lawn green measures 1.67:1 against it and
   1.4.11 asks 3:1, so the hero gets its own pair rather than the shared one.
   Here rather than beside .btn-light for the usual reason: .btn-light is a
   single class and so is this, and source order settles the tie. */
.hero .btn-light{background:var(--sk-hero-cta-bg);color:var(--sk-hero-cta-ink)}
.hero .btn-light:hover{background:var(--sk-hero-cta-hover,var(--sk-hero-cta-bg));
  filter:brightness(1.12)}
.planband .kicker{color:var(--sk-panel-kicker)}
.pricecard .kicker{color:var(--sk-panel-kicker)}
