/* Hand-written overrides for this migration — client-hhwt.
 *
 * DELIBERATELY OUTSIDE site/public/styles/ (gotcha 49): tools/port-css.mjs owns
 * that directory and clears its own hashed outputs on every run, so a hand-written
 * sheet placed there is deleted by the next port with no error anywhere — on one
 * site that silently removed the sheet that hides the inactive device bands, and
 * all three headers then rendered at every width.
 *
 * EVERY NUMBER BELOW WAS CENSUSED ON THIS SITE. That sentence is here because the
 * file this was carried forward from was byte-identical to client-naturescapelawn's
 * and every count in its prose belonged to that client or to one before it — a blog
 * declaring 29 with 10 visible, ".flexslider on 6 of 31 pages", "27 of 31 pages"
 * carry an accordion, "93 captures", and a paragraph about a slider on
 * insulationspecialistusa.net. None of that is true here. The RULES were sound and
 * are kept; the arithmetic around them is re-derived.
 *
 * Population: 342 captures (114 pages x 3 bands) plus all 114 raw served documents.
 *
 * Keep this file minimal. Anything that can come from the ported cascade should.
 */

/* The honeypot. Positioned off-screen rather than display:none — bots skip
 * obviously-hidden fields, and taking it out of flow means it costs no layout,
 * which the pixel gate would otherwise measure. Live has NO honeypot on this
 * site's ONE form (/contact — censused at 1 of 114 in both the export and the raw
 * served html), so this is ours and must cost nothing. */
.mg-hp {
  position: absolute !important;
  left: -9999px !important;
  top: auto !important;
  width: 1px !important;
  height: 1px !important;
  overflow: hidden !important;
}

/* SPECIFICITY NOTE — read before editing any rule below.
 *
 * Duda emits a per-widget rule for EVERY widget on the site, shaped
 *
 *     #dm .dmBody div.u_1213107879 { display: block !important }
 *
 * That is specificity (1,2,1) and it is `!important`. A hide written as a plain
 * `.mg-only-t { display:none !important }` is (0,1,0), so `!important` on both
 * sides means SPECIFICITY decides and Duda's rule wins — the element stays visible
 * and nothing in the console says so.
 *
 * So every gate below repeats its class three times behind `#dm`, giving (1,3,0),
 * which outranks (1,2,1) on class count without relying on source order. Do not
 * "tidy" the repetition away.
 */

/* ---------------------------------------------------------------------------
 * BLOG PAGINATION — ONE CONTROL SHAPE HERE, NOT BOTH.
 *
 * The copied version of this block described a site carrying BOTH of Duda's
 * controls at once. Censused on the raw served html of all 114 pages, this site is
 * uniformly the NUMBERED PAGER — there is no show-more control anywhere:
 *
 *   /blog            declared 20, visible 10  NUMBERED PAGER, REPLACES
 *                    <nav class="pagination-nav"> with data-action="paginate",
 *                    POSTing /_dm/s/rt/scripts/ajax_widj_refresh.jsp and SWAPPING
 *                    the card set. more-posts x0, pagination-nav x1.
 *
 *   20 blog posts    declared 19, visible 10  the SAME numbered pager, one per post
 *                    ("related articles"). more-posts x0, pagination-nav x1 on all 20.
 *
 * 21 pages carry a paginated blog widget in total. blog.rss independently declares
 * 20 items, which is the cross-check gotcha 83 requires — a source that cannot agree
 * with our own capture by construction. Driving the pager on live recovered 63 of 63
 * records (21 pages x 3 devices) with 0 incomplete and the identity assertion PASS.
 *
 * A static build has no Duda backend, so every card ships and the ones past the
 * first page are stamped mg-blog-hidden with data-mg-blog-page; runtime.js reveals
 * or swaps them in live's own step size. This rule is what hides them initially.
 * ------------------------------------------------------------------------- */
#dm .mg-blog-hidden.mg-blog-hidden.mg-blog-hidden {
  display: none !important;
}

/* display:none DOES NOT CHANGE CHILD INDICES, so shipping the withheld cards
 * silently moves which card is :last-child — gotcha 69.
 *
 * Duda's own rule is `.postArticle:not(:last-child){padding-bottom:Npx}`. On live
 * the last VISIBLE card is genuinely :last-child and takes none of it. In our build
 * it is followed by hidden siblings, so it stops matching and GAINS that padding,
 * pushing the control and the whole footer down by a constant amount at every width
 * — the signature of one shared element rather than a per-page fault.
 *
 * Keyed on `:has(+ .mg-blog-hidden)` rather than on a stamped class, because that
 * tracks the SEQUENCE as batches are revealed instead of pinning the initial state;
 * runtime.js does not have to re-stamp anything as cards appear. */
#dm .postArticle:not(.mg-blog-hidden):has(+ .postArticle.mg-blog-hidden) {
  padding-bottom: 0;
}

/* ---------------------------------------------------------------------------
 * PER-DEVICE WIDGET FORKS.
 *
 * THE DEVICE FORK ON THIS SITE IS TWO-WAY, MEASURED: the tablet document is
 * BYTE-IDENTICAL to the desktop one on 8 of 8 pages sampled, and only the phone
 * document differs (dmtemplateid mobileHamburgerLayout against Hamburger). The
 * `mg-only-t` gate is therefore expected to be unused or to carry a copy identical
 * to `mg-only-d`; it is kept because build-pages.py emits three pieces wherever the
 * captures disagree, and a gate that is present and unused costs nothing while a
 * missing one is silent.
 *
 * The band boundaries are Duda's own, read out of the ported cascade and confirmed
 * against the hamburger sweep: mobile <=767, tablet 768-1024, desktop >=1025. Note
 * 1025 rather than 1024+1 by assumption — the horizontal nav has width 0 up to 1024
 * and first paints at 1025, and the hamburger button is display:flex up to 1024 and
 * none from 1025.
 *
 * These hide rather than remove, deliberately — the elements stay in the document
 * so the runtime can still address them, and display:none costs no layout.
 *
 * Only the INACTIVE bands are hidden, inside the media queries. The active band is
 * never touched, so it keeps whatever `display` the ported cascade gives it — an
 * earlier version hid all three and restored one with `display: revert`, which
 * reverts past the author cascade to the UA default and would have replaced the
 * widget's real display value with a plain `block`.
 * ------------------------------------------------------------------------- */
@media (max-width: 767px) {
  #dm .mg-only-d.mg-only-d.mg-only-d,
  #dm .mg-only-t.mg-only-t.mg-only-t {
    display: none !important;
  }
}

@media (min-width: 768px) and (max-width: 1024px) {
  #dm .mg-only-d.mg-only-d.mg-only-d,
  #dm .mg-only-m.mg-only-m.mg-only-m {
    display: none !important;
  }
}

@media (min-width: 1025px) {
  #dm .mg-only-t.mg-only-t.mg-only-t,
  #dm .mg-only-m.mg-only-m.mg-only-m {
    display: none !important;
  }
}

/* ---------------------------------------------------------------------------
 * NO SLIDER RULES — BUT FOR A DIFFERENT REASON THAN ON THE SITE THIS FILE CAME
 * FROM, AND THE INHERITED TEXT WAS FALSE HERE.
 *
 * The copied version of this block read ".flexslider 0, ssrimageslider 0" and
 * concluded there was nothing to rule on. THIS SITE HAS BOTH. Censused over all
 * 114 raw served documents and all 342 captures:
 *     .flexslider            87 of 114 pages  (1 slider on 64, 2 on 23)
 *     ssrimageslider          1 of 114 pages  (the home page)
 *     .bgGallerySlide         0
 *     [data-gallery-bg]       0
 *     <video autoplay>        0
 *
 * No rule is needed anyway, and THAT is the measurement. Every .flexslider here
 * declares, in its own sliderscriptparams:
 *     isAutoPlay:false  directionNav:false  controlNav:false  isFade:true
 * and carries EXACTLY ONE <li> with zero clones. A single-slide fade slider with
 * autoplay off has nothing to advance to and no control to advance it, so the
 * ported cascade renders it correctly at rest. It also means these sliders need no
 * gate --hide: the usual "a carousel never diffs to zero" reasoning does not apply,
 * and excluding them would give up pixel coverage for nothing.
 *
 * `ed-version` stays on every .flexslider (gotcha 28) — it reads like editor
 * metadata and is load-bearing.
 *
 * The ssrimageslider on the home page is a DIFFERENT widget: 7 slide slots, and it
 * CAN advance under a scrolling capture (gotcha 46). Decided from the captures.
 *
 * the widget PAINTS in its settled state and the pixel gate scores the page 0.000%
 * — while every click is inert.
 *
 * CENSUSED HERE, on data-element-type rather than on a class token (styled
 * components generate their class names, so a class regex finds nothing on these):
 * an ssraccordion is on 58 of 114 pages, across 76 DISTINCT widget ids. Unlike the
 * site this file came from, pages here carry MORE THAN ONE: 34 pages have a single
 * root, 11 have 2, 9 have 3, and two pages each have 4 and 5. Items per page run
 * from 2 to 19, most commonly 5 (24 pages); /faq carries 15 in one root and the home
 * page three roots of 5, 2 and 5.
 *
 * LIVE'S ACCORDION WORKS, so ours must too — NOT gotcha 91's inert case. Measured on
 * live at 1440 with a REAL hit-tested click (elementFromPoint at the title's centre
 * returns SPAN.sc-eqUAAy, so the click demonstrably lands): every panel is 0/0 at
 * rest, and clicking item 1 opens it to 96/96 on /faq and 64/64 on home, leaving its
 * siblings closed.
 *
 * THE CLOSED RULE IS IN THE PORT AND THE OPEN RULE IS NOT, AND THAT IS NOT A
 * PORTING MISTAKE. styled-components insert through the CSSOM, so a class exists in
 * a document's sheet only if that component actually RENDERED with it. Measured:
 * the closed state
 *
 *     .dygwmn { overflow:hidden; transition:max-height .3s ease-out;
 *               height:auto; max-height:0px; }
 *
 * appears 537 times across the captures, while the OPEN class is minted per render
 * — ELEVEN distinct names seen (ctGkua, iWTYns, jrXmxD, lkQkcT, hVjNUn, jCZtbv,
 * cpMcTW, gqFKNX, CoQjN, lbMDqK and more), differing by page AND by device. So no
 * open-state name can be hardcoded, and restoring the rule under OUR OWN class is
 * recovering a sheet the live read could not reach.
 *
 * The transition runs on MAX-HEIGHT, so opening means raising it.
 *
 * Repeated behind #dm to clear Duda's own per-widget !important display rules.
 * ------------------------------------------------------------------------- */
#dm .mg-acc-open.mg-acc-open.mg-acc-open {
  max-height: 2000px;
}


/* ============================ REVIEW PASS 2026-09-11 ============================
 * Everything below was added by the pre-launch QA pass. Each rule carries the
 * measurement that justifies it.
 */

/* --- Caret tap target in the drawer's submenus. -------------------------------
 * The caret is an inline <span class="icon"> inside the label's anchor. Sizing IT
 * to 44px makes a box wide enough to wrap the line and leaves the caret floating
 * above-right of its label. An invisible ::after keeps the caret's inline
 * footprint and still gives a real touch area. runtime.js tags it. */
#dm .layout-drawer .unifiednav__item_has-sub-nav .icon.mg-subnav-caret {
  position: relative;
}
#dm .layout-drawer .unifiednav__item_has-sub-nav .icon.mg-subnav-caret::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}
/* A closed group must collapse its PADDING as well as its height: max-height
 * clips the content box only, and Duda pads the sub-menu with
 * `[data-auto="sub-pages"] { padding… !important }`, so without this a "closed"
 * group still stands ~20px tall with a sliver of the panel showing. */
#dm .layout-drawer .unifiednav__item-wrap:not(.unifiednav__item-wrap_open) > .unifiednav__container.mg-subnav {
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  border-top-width: 0 !important;
  border-bottom-width: 0 !important;
}
/* The caret flips while its group is open. Rotating `.icon` itself loses to a
 * per-band transform already on it, so the glyph (`::before`) turns instead. */
#dm .layout-drawer .unifiednav__item-wrap .icon.mg-subnav-caret::before {
  display: inline-block;
  transition: transform .3s ease-out;
}
#dm .layout-drawer .unifiednav__item-wrap.unifiednav__item-wrap_open > a .icon.mg-subnav-caret::before {
  transform: rotate(180deg) !important;
}
/* Duda pre-rotates the caret of the CURRENT section's parent
 * (`.unifiednav__item.dmNavItemSelected > .nav-item-text .icon::before`), because
 * live ships that group expanded. With every group now starting closed, that
 * left an up-pointing caret on a shut group (measured: PAINTING on
 * /exterior-painting-durango-co). The caret follows the group's real state. */
#dm .layout-drawer .unifiednav__item-wrap:not(.unifiednav__item-wrap_open) > a .icon.mg-subnav-caret::before {
  transform: none !important;
}

/* --- Blog pager: EVERY NUMBER EXCEPT THE ACTIVE ONE IS WHITE ON WHITE. ---------
 * Duda styles only the active item and leaves `.pagination-link { color: inherit }`.
 * Measured on all 21 pagers (the blog index and every post) at 1440:
 *     active "1"    21.00:1
 *     page   "2"     1.00:1  (white on white)
 *     next arrow     1.64:1
 * A visitor sees a lone "1" and no way to page 2 — and live has the identical
 * fault, which is why it is not a design choice worth reproducing. Scoped
 * :not(.active):not(.disabled) so the active pill and the greyed-out end arrow
 * keep their own treatment. */
#dm .pagination-nav .pagination-link:not(.active):not(.disabled),
#dm .pagination-nav .pagination-link:not(.active):not(.disabled) * {
  color: #6b6b6b !important;
}
/* The active number's own ink is near-black (21:1) with no visible pill on this
 * site, so once the others are legible it needs weight to stay distinguishable:
 * inactive #6b6b6b is 5.33:1 (AA), active stays black and goes bold. */
#dm .pagination-nav .pagination-link.active {
  font-weight: 700;
}

/* --- The GBP map embed is 350px tall; the house cap is 300px. ------------------
 * The iframe is height:100% of a widget box that Duda sizes to 350px (302px on
 * phone), so capping the iframe alone left a 50px empty band under the map —
 * measured iframe 300 / parent 350 at 1440 and 768. The box is capped with it. */
#dm iframe[src*="google.com/maps/embed"],
#dm :has(> iframe[src*="google.com/maps/embed"]) {
  max-height: 300px !important;
}

/* --- Blog search results line ("Search results for 'x' (n)", as live prints). */
#dm .mg-search-summary {
  text-align: center;
  margin: 12px 0 0;
  font-size: 18px;
  line-height: 1.4;
}

/* --- Legal pages: Duda's reset zeroes margin on p, ul and h2, so a page built
 * from the template renders as one slab with each heading pressed against the
 * paragraph above it. The rhythm is set explicitly. */
#dm .mg-legal-body { text-align: left; }
#dm .mg-legal-body .mg-legal-h2 {
  margin: 34px 0 10px !important;
  font-size: 24px;
  line-height: 1.3;
  font-weight: 700;
}
#dm .mg-legal-body .mg-legal-h2:first-child { margin-top: 0 !important; }
#dm .mg-legal-body .mg-legal-p {
  margin: 0 0 14px !important;
  line-height: 1.65;
}
#dm .mg-legal-body .mg-legal-ul {
  margin: 0 0 14px 22px !important;
  padding-left: 0;
  list-style: disc;
}
#dm .mg-legal-body .mg-legal-ul li {
  margin: 0 0 6px !important;
  line-height: 1.6;
  list-style: disc;
}
#dm .mg-legal-body a { text-decoration: underline; }

/* --- Legal links: footer and contact form. -------------------------------------
 * FOOTER. Follows the copyright block's own alignment, measured per band at
 * 1440 / 768 / 390: text-align:center on all three, white rgb(255,255,255) on
 * the copyright strip's blue rgb(0,94,200). The ink is written down explicitly —
 * an element added beside .copyright inherits the column's ink, not the strip's.
 *
 * FORM. One line, the two labels, not a sentence. `clear: both` because Duda's
 * submit wrapper (.dmformsubmit.dmWidget.R) is FLOATED and a following block
 * otherwise flows up alongside it despite sitting last in the markup. The space
 * above is PADDING, not margin: a margin collapses against the preceding widget's
 * box and measures 0px while the CSS claims otherwise. */
#dm .mg-legal-footer {
  text-align: center;
  font-size: 13px;
  line-height: 1.5;
  padding: 4px 0 2px;
  color: rgb(255, 255, 255);
}
#dm .mg-legal-footer a,
#dm .mg-legal-footer .mg-legal-sep {
  color: rgb(255, 255, 255) !important;
}
#dm .mg-legal-footer a { text-decoration: underline; }
#dm .mg-legal-form {
  clear: both;
  display: block;
  width: 100%;
  padding-top: 15px;
  font-size: 14px;
  line-height: 1.5;
  text-align: left;
  white-space: nowrap;
}
#dm .mg-legal-form a { text-decoration: underline; }

/* --- Business-name interlinks added by the QA pass. ---------------------------
 * The house rule: on every page except home, the business name in body copy
 * links to the homepage at least once. Bold + underline, colour inherited so it
 * sits in whatever ink its paragraph already uses. Body copy only. */
#dm a.mg-interlink {
  color: inherit !important;
  font-weight: 700;
  text-decoration: underline;
}

/* --- Lightbox share button. PhotoSwipe's share menu is driven by its own UI
 * script, which the port does not ship, so the button painted in the top bar
 * and did nothing when tapped. A dead control is worse than none; the close
 * button, counter and both arrows stay. */
.pswp .pswp__button--share { display: none !important; }
