/* Hand-written overrides for this migration.
 *
 * 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.
 *
 * 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. */
.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.
 *
 * Measured: with the plain selector, /blog rendered all THREE per-device widget
 * copies at once — 30 cards against live's 10, document height 7640px against
 * live's 3548px, and the gate scored 59.4%.
 *
 * 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 index: THIS SITE DOES HAVE A PAGER, and it withholds most of the posts.
 *
 * Measured on live: /blog declares data-paginate-total-elements="29" and
 * visible-items="10", and renders exactly 10 distinct data-blog-post-alias values.
 * The control is an APPEND-style <a class="more-posts-text-container"> ("Show
 * More"), not a numbered nav — driving it goes 10 -> 20 -> 29, after which the
 * control removes itself. blog.rss independently declares 29 items.
 *
 * A static build has no Duda backend, so all 26 cards ship and the extras are
 * stamped mg-blog-hidden with data-mg-blog-page in live's own batch size;
 * runtime.js reveals them a batch at a time. This rule is what hides them
 * initially, so unlike the port it came from it is NOT a no-op here. */
#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.
 *
 * Duda's own rule is `.postArticle:not(:last-child){padding-bottom:Npx}` — this
 * cascade carries it at 100px, 20px and 10px for the three bands. On live the
 * 10th card IS the last child and takes none of it. In our build the 10th card is
 * followed by 19 hidden siblings, so it stops matching :last-child 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.
 *
 * Specificity: Duda's rule is (0,2,1) and not !important, so this (0,4,1) plain
 * rule wins on merit without needing one. */
#dm .postArticle:not(.mg-blog-hidden):has(+ .postArticle.mg-blog-hidden) {
  padding-bottom: 0;
}

/* PER-DEVICE WIDGET FORKS.
 *
 * Duda builds .dmPhotoGallery and .mainBlog client-side and the three device
 * documents genuinely differ, so build-pages.py emits each widget three times and
 * stamps the copies mg-only-d / mg-only-t / mg-only-m. Without these rules ALL
 * THREE render at every width — which is exactly what the first gate run showed:
 * / came back at 77% with a +10458px height delta and /blog at 62% with +10150,
 * both of them simply the same widget painted three times over.
 *
 * The band boundaries are Duda's own, read out of the ported cascade: mobile
 * <=767, tablet 768-1024, desktop >=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;
  }
}

/* MEDIA-SLIDER FILM ROLE — NO OVERRIDE, and that is the measured answer.
 *
 * An earlier revision added a filmstrip rule here, reasoning from the SERVED html
 * where the film role carries `.gDtRCy` with position:absolute, right:-200%,
 * flex-direction:row and translateX(-0%). That was WRONG, and it is recorded
 * rather than quietly deleted because the reasoning is seductive.
 *
 * The served html is the authority for what the RUNTIME WRITES, but this rule is
 * not something the runtime writes — it belongs to the pre-hydration render. Once
 * the component hydrates it re-renders under a different generated class and the
 * filmstrip rule is simply not applied.
 *
 * MEASURED on live, settled, at 1440:
 *     class="sc-gEvEer jKjUdt"  position:relative  display:block
 *     left:0px  right:0px  transform:none  children:2
 *
 * which is precisely what this build already produced WITHOUT any override. So
 * the correct action here is to add nothing.
 *
 * INHERITED CONTEXT, RETARGETED TWICE NOW. The paragraph that stood here described
 * a slider on a client two migrations back, and the retarget before this one
 * replaced it with a claim about cristosgaragedoors' /reviews page — a page this
 * site does not have. Neither was ever a fact about insulationspecialistusa.net.
 * The general lesson both recorded still holds and is why the block above adds
 * nothing: a slider showing a different photograph than live is usually the CAPTURE
 * landing on a different slide, not a CSS defect, and the remedy is to exclude it
 * from the pixel gate and verify it per element.
 *
 * CENSUSED ON THIS SITE (tools/census-structure.mjs, 93 captures, <style>/<script>
 * stripped first): .flexslider is on 6 of 31 pages, identically on all three bands.
 * There are ZERO .bgGallerySlide elements on any band, and exactly ONE photo
 * gallery, on a single page. Whether any of them auto-advances is measured BEFORE
 * anything is excluded,
 * and each exclusion is recorded in visual-diff.config.json's hiddenRegions with the
 * measurement behind it — a hidden region gets no pixel coverage at all, so the
 * per-element check is the only coverage it has.
 */

/* ---------------------------------------------------------------------------
 * ACCORDION OPEN STATE — recovering a rule the live CSS read could not see.
 *
 * Duda's FAQ accordion is a styled-component driven by Duda's runtime, which is
 * correctly not ported. The markup and the ported cascade both arrive intact, so
 * the widget PAINTS in its settled state and the pixel gate scores the page
 * 0.000% — while every click is inert. CENSUSED HERE: an accordion (matched on
 * data-grab, not on a class token — styled-components generate their class names,
 * so a class regex finds nothing on these) is present on 27 of 31 pages, identically
 * on all three bands.
 *
 * The state is a generated class on the panel host, and the two names are NOT
 * hardcoded here for the reason gotcha 75 gives: styled-components mint a fresh
 * instance name per render. What IS measured is the mechanism —
 *
 *     closed: .dygwmn { overflow:hidden; transition:max-height .3s ease-out;
 *                       height:auto; max-height:0px; }
 *
 * — so the transition runs on MAX-HEIGHT, and opening means raising it.
 *
 * THE OPEN RULE IS ABSENT FROM THE PORTED CASCADE, and that is not a porting
 * mistake. styled-components insert their rules through the CSSOM, so a class
 * only exists in a document's sheet if that component actually RENDERED with it.
 * No panel is ever open on a freshly loaded page, so `.gnWefg` — the open class
 * live uses — appears in ZERO of the ported sheets while `.dygwmn` appears in 4.
 * Restoring it is recovering a sheet the live read could not reach, which is the
 * same situation gotcha 46 describes for a slider's resting state, arrived at
 * from the opposite direction.
 *
 * Keyed on our own class rather than Duda's generated one so nothing here depends
 * on a name that changes per render. Repeated behind #dm to clear Duda's own
 * per-widget `!important` display rules (gotcha 70).
 *
 * Lives in gm/, NOT in styles/: port-css cleans its own hashed output from that
 * directory on every run and would take this file with it (gotcha 49).
 * ------------------------------------------------------------------------- */
#dm .mg-acc-open.mg-acc-open.mg-acc-open {
  max-height: 2000px;
}


/* ---------------------------------------------------------------------------
 * CURRENT-PAGE AFFORDANCE IN THE DESKTOP NAV.
 *
 * MEASURED on the build AND on live, /about /gallery /faq at 1440: all seven
 * top-level items render rgb(33,33,33), weight 400, no underline, no border —
 * the item for the page you are ON is styled identically to every other item,
 * and hovering changes nothing either. So there is no "where am I" signal at all.
 * Live has the same gap; per the workflow this is a house standard, added rather
 * than reproduced.
 *
 * SCOPED TO THE HORIZONTAL NAV. The two drawer navs are data-nav-structure
 * "VERTICAL" and already give the current item a filled yellow pill; adding this
 * there would double up on it.
 *
 * Drawn as a background gradient rather than a border-bottom so nothing reflows
 * when it appears — a border would shift every sibling item by 2px on hover.
 * ------------------------------------------------------------------------- */
#dm .main-navigation.unifiednav[data-nav-structure="HORIZONTAL"] a.unifiednav__item {
  background-image: linear-gradient(var(--color_2), var(--color_2));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0 2px;
  transition: background-size .2s ease-out;
}
#dm .main-navigation.unifiednav[data-nav-structure="HORIZONTAL"] a.unifiednav__item:hover,
#dm .main-navigation.unifiednav[data-nav-structure="HORIZONTAL"] a.unifiednav__item:focus-visible {
  background-size: 100% 2px;
}
/* The page the visitor is actually on keeps it without hovering. */
#dm .main-navigation.unifiednav[data-nav-structure="HORIZONTAL"] a.unifiednav__item.currentPage {
  background-size: 100% 2px;
  font-weight: 700;
}

/* ---------------------------------------------------------------------------
 * DRAWER SUBMENU CARETS (tablet + phone).
 *
 * The toggle itself is bound in runtime.js — see the long note there for why the
 * inherited handler bound nothing. This supplies the two visual halves.
 *
 * TAP TARGET VIA AN ::after OVERLAY, NOT BY SIZING THE CARET. The caret is an
 * inline <span class="icon"> inside the label's anchor; giving it a 44px
 * min-width/min-height makes it wide enough to wrap and the carets end up
 * floating above and to the right of their labels. The overlay gives it a real
 * touch area while the caret keeps its inline footprint.
 *
 * ROTATE `.icon::before`, NOT `.icon`. The icon span already carries a per-band
 * `transform: scale()`; a transform on the span itself silently replaces it.
 * ------------------------------------------------------------------------- */
#dm .layout-drawer .main-navigation.unifiednav a.unifiednav__item .icon.mg-caret {
  position: relative;
  cursor: pointer;
}
#dm .layout-drawer .main-navigation.unifiednav a.unifiednav__item .icon.mg-caret::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}
#dm .layout-drawer .main-navigation.unifiednav a.unifiednav__item .icon.mg-caret::before {
  display: inline-block;
  transition: transform .3s ease-out;
}
#dm .layout-drawer li.mg-sub-open > a.unifiednav__item .icon.mg-caret::before {
  transform: rotate(180deg);
}
/* The <ul> is driven by an inline max-height from its own scrollHeight; this is
 * only the timing. Padding animates with the height so a collapsed group cannot
 * leave its own vertical padding standing as a sliver under the parent. */
#dm .layout-drawer ul.unifiednav__container_sub-nav {
  transition: max-height .3s ease-out, opacity .3s ease-out,
              padding-top .3s ease-out, padding-bottom .3s ease-out;
}

/* ---------------------------------------------------------------------------
 * PHOTO GALLERY LIGHTBOX — added 2026-09-09, a deliberate improvement over live.
 *
 * Live opens nothing on any gallery (re-measured by hit-tested clicks on / and
 * /gallery; see the note in runtime.js). This is our own overlay, NOT Duda's
 * .pswp shell, which ships position:absolute and a --hidden UI class.
 *
 * position: FIXED is the point of it. An absolutely-positioned overlay anchors to
 * the document, so a visitor who opens a gallery halfway down the page gets the
 * whole lightbox painted a screen or two above where they are looking — which
 * reads as "clicking does nothing" while every class and opacity check passes.
 * ------------------------------------------------------------------------- */
#dm .dmPhotoGallery a { cursor: zoom-in; }

.mg-lb {
  position: fixed;
  inset: 0;
  z-index: 2000000000; /* over #dmPopupMask (1000000999) and the drawer */
  display: none;
  background: rgba(20, 13, 7, .92);
  align-items: center;
  justify-content: center;
}
.mg-lb.mg-lb--open { display: flex; }
html.mg-lb-lock, html.mg-lb-lock body { overflow: hidden; }

.mg-lb__stage {
  margin: 0;
  max-width: 92vw;
  max-height: 88vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
.mg-lb__img {
  max-width: 92vw;
  max-height: 88vh;
  width: auto;
  height: auto;
  display: block;
  object-fit: contain;
  box-shadow: 0 10px 40px rgba(0, 0, 0, .5);
  border-radius: 4px;
}
/* seo.mjs stamps width/height on every <img> for CLS, and Duda's own sheet then
 * matches img[width][height] — neither may size the lightbox photo. */
#dm .mg-lb__img, .mg-lb__img[width][height] { width: auto !important; height: auto !important; }

.mg-lb__close,
.mg-lb__nav {
  position: absolute;
  background: rgba(0, 0, 0, .45);
  color: var(--color_2, #f3cf55);
  border: 0;
  cursor: pointer;
  line-height: 1;
  border-radius: 50%;
  /* A real 44px touch target on the CONTROL, which is a button and can take one —
   * unlike the nav caret, which is an inline span and wraps if sized. */
  min-width: 48px;
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background .2s ease-out, transform .2s ease-out;
}
.mg-lb__close:hover, .mg-lb__nav:hover { background: rgba(0, 0, 0, .75); transform: scale(1.08); }
.mg-lb__close:focus-visible, .mg-lb__nav:focus-visible { outline: 2px solid var(--color_2, #f3cf55); outline-offset: 3px; }

.mg-lb__close { top: 16px; right: 16px; font-size: 34px; }
.mg-lb__nav { top: 50%; transform: translateY(-50%); font-size: 40px; padding-bottom: 6px; }
.mg-lb__nav:hover { transform: translateY(-50%) scale(1.08); }
.mg-lb__prev { left: 16px; }
.mg-lb__next { right: 16px; }
.mg-lb--single .mg-lb__nav { display: none; }

.mg-lb__counter {
  position: absolute;
  bottom: 18px;
  left: 50%;
  transform: translateX(-50%);
  color: #fff;
  font-size: 14px;
  letter-spacing: .04em;
  background: rgba(0, 0, 0, .45);
  padding: 5px 14px;
  border-radius: 999px;
}
@media (max-width: 767px) {
  .mg-lb__close { top: 10px; right: 10px; }
  .mg-lb__prev { left: 6px; }
  .mg-lb__next { right: 6px; }
  .mg-lb__stage, .mg-lb__img { max-width: 96vw; max-height: 78vh; }
}

/* ---------------------------------------------------------------------------
 * LEGAL PAGES (/privacy-policy, /terms-conditions) — built, not ported.
 *
 * Three things the donor shell does not supply, because its own rows carried
 * their own widths, spacing and hero image:
 *
 * 1. HEADER CLEARANCE. This site is `header-over-content`, so the header floats
 *    on top of the first row. The donor's hero began with a photo that absorbed
 *    that; a bare text row starts underneath the nav — measured, the first H2 sat
 *    behind the logo.
 * 2. A READABLE MEASURE. Unconstrained, the copy ran the full 1440 viewport.
 * 3. A VERTICAL RHYTHM. Duda's reset zeroes margin on p, ul and h2, so every
 *    block sat 8px from its neighbour and each heading was pressed against the
 *    paragraph above it — MEASURED at a flat 8px for all 11 sampled gaps.
 * ------------------------------------------------------------------------- */
/* SPECIFICITY MATTERS HERE, and the first attempt lost twice:
 *   #dm .dmContent .dmRespRow            { padding: 120px 40px }   (1,0,2)
 *   .dmRespColsWrapper                   { max-width: 1440px !important }
 * beat `#dm .gy-legal-hero` (1,0,1) and a plain max-width. The result was a
 * 120px bottom pad on the hero plus a 120px top pad on the body — a MEASURED
 * 258px void between "Effective Date" and the first paragraph at 1440 (118px at
 * 390) — and full-bleed body copy. Matched on both counts below. */
#dm .dmContent .dmRespRow.gy-legal-hero { padding: 150px 40px 0; }
#dm .dmContent .dmRespRow.gy-legal-body { padding: 22px 40px 90px; }
#dm .dmContent .gy-legal-hero > .dmRespColsWrapper,
#dm .dmContent .gy-legal-body > .dmRespColsWrapper {
  max-width: 900px !important;
  margin: 0 auto;
}

#dm .gy-legal-body .dmNewParagraph + .dmNewParagraph { margin-top: 14px; }
/* On the WRAPPER, because `#dm h2 { margin: 0 !important }` in the ported reset
 * beats a margin on the heading itself — measured as a flat 14px everywhere
 * until this moved off the h2 and onto its .dmNewParagraph. */
#dm .gy-legal-body .dmNewParagraph.gy-h2 + .dmNewParagraph,
#dm .gy-legal-body .dmNewParagraph + .dmNewParagraph.gy-h2 { margin-top: 40px; }
#dm .gy-legal-body .dmNewParagraph + .dmNewParagraph.gy-h3 { margin-top: 26px; }
#dm .gy-legal-body .dmNewParagraph.gy-h2 + .dmNewParagraph { margin-top: 10px; }
#dm .gy-legal-body .dmNewParagraph.gy-h3 + .dmNewParagraph { margin-top: 8px; }

#dm .gy-legal-body ul.gy-legal-list {
  list-style: disc outside;
  padding-left: 26px;
  margin: 6px 0 0;
}
#dm .gy-legal-body ul.gy-legal-list li { margin: 0 0 6px; }
#dm .gy-legal-hero h1 { margin-bottom: 6px; }

@media (max-width: 767px) {
  #dm .dmContent .dmRespRow.gy-legal-hero { padding: 110px 20px 0; }
  #dm .dmContent .dmRespRow.gy-legal-body { padding: 18px 20px 60px; }
}

/* ---------------------------------------------------------------------------
 * LEGAL LINKS — footer and contact form. Injected in Page.astro; see the note
 * there. The two live on OPPOSITE backgrounds, which is exactly why they get
 * separate colour rules rather than one shared one.
 * ------------------------------------------------------------------------- */

/* FOOTER: white on rgb(84,55,30). Colour is stated, never inherited — an <a>
 * takes its colour from the sheet's link rule, not from .copyright's white. */
/* Its own line under the copyright, centred to match it. Set explicitly: the
 * block is a SIBLING of .copyright, so it is outside the flex container that
 * centres the copyright's own children. */
#dm .gy-footer-legal { margin-top: 6px; text-align: center; }
#dm .gy-footer-legal,
#dm .gy-footer-legal span { color: #fff; }
#dm .gy-footer-legal a {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 2px;
}
#dm .gy-footer-legal a:hover,
#dm .gy-footer-legal a:focus-visible { color: var(--color_2, #f3cf55); }

/* CONTACT FORM: sits on the page's own light ground, so it takes dark ink.
 *
 * clear:both IS THE POINT. Duda wraps the submit in .dmformsubmit.dmWidget.R,
 * which is floated, so without this the line flows up ALONGSIDE the button and
 * lands beside the last field while still being last in the markup.
 *
 * PADDING, NOT MARGIN, for the ~15px of space above it: a margin collapses
 * against the preceding widget's own box and measures 0. */
#dm .gy-form-legal {
  clear: both;
  padding-top: 15px;
  /* one line, not a sentence */
  white-space: nowrap;
  font-size: 14px;
  color: #333;
}
#dm .gy-form-legal a {
  color: #333;
  text-decoration: underline;
  text-underline-offset: 2px;
}
#dm .gy-form-legal a:hover,
#dm .gy-form-legal a:focus-visible { color: var(--color_1, #54371e); }

/* ---------------------------------------------------------------------------
 * /contact-us-now-to-get-a-free-estimate — H1 RESTORED.
 *
 * The page shipped with ZERO h1: its title "Contact Us Now to Get a Free
 * Estimate" was an <h3>, and the only other heading is the form's own hidden
 * "Contact Us". Live has the same gap. Retagged to h1 (house standard: exactly
 * one per page) and PINNED so the retag is invisible.
 *
 * MEASURED on the h3 before the change, per band — and the line-height is pinned
 * alongside the size, because pinning font-size alone still lets the h1's own
 * line-height through and moves the band:
 *      desktop / tablet   32px / 38.4px      phone   24px / 28.8px
 *      weight 400, Merriweather, rgb(84,55,30), letter-spacing 1.6px, margin 0
 * ------------------------------------------------------------------------- */
/* !important, because the ported h1 rule carries its own and won at 56px/67.2px
 * on the first attempt — the retag had visibly grown the band before this. */
#dm .dmBody h1.gy-h1-pin.gy-h1-pin {
  font-size: 32px !important;
  line-height: 38.4px !important;
  font-weight: 400 !important;
  letter-spacing: 1.6px !important;
  color: rgb(84, 55, 30) !important;
  margin: 0 !important;
}
@media (max-width: 767px) {
  #dm .dmBody h1.gy-h1-pin.gy-h1-pin {
    font-size: 24px !important;
    line-height: 28.8px !important;
  }
}

/* Blog search results note — see the blogSearch section in runtime.js. */
#dm .gy-search-note {
  padding: 0 0 14px;
  font-size: 16px;
  color: var(--color_1, #54371e);
  text-align: center;
}
#dm .dmBlogSearchInput { cursor: text; }
#dm .dmBlogSearchIcon { cursor: pointer; }

/* ---------------------------------------------------------------------------
 * BODY-COPY INTERLINK (business name -> homepage).
 *
 * Underline + weight, and ONLY here: the class is applied by
 * tools/add-interlinks.py inside <p> blocks only, so headings, buttons and
 * header/footer content keep their own treatment. Colour is the brand brown so
 * the link reads as a link against body ink without shouting.
 * ------------------------------------------------------------------------- */
#dm .gy-interlink {
  color: var(--color_1, #54371e);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
}
#dm .gy-interlink:hover,
#dm .gy-interlink:focus-visible { text-decoration-thickness: 2px; }
