/* Hand-written overrides for this migration.
 *
 * NO PREVIOUS CLIENT IS NAMED IN THIS FILE, DELIBERATELY: it is copied verbatim into
 * dist, so a prior client's name here would be PUBLISHED on this client's domain and
 * would make a residue sweep report a correct file dirty. Provenance lives in
 * MIGRATION-NOTES, which does not ship.
 *
 * 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.
 *
 * EVERY NUMBER BELOW WAS CENSUSED ON THIS SITE ON 2026-09-29 (gotcha 113: this file arrived
 * carrying an earlier site's census in prose that read as measurement; the RULES were kept,
 * the prose re-taken). Population, DERIVED: pages.txt holds 13 paths, so 39 captures
 * (13 x 3 bands) plus 39 served documents (13 x 3 device UAs), plus the 404.
 *
 * 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-us — generated/form-pages.json, 1 of 13 pages); its reCAPTCHA is removed here
 * (gotcha 17: its keys are Duda's own and domain-restricted). So this is ours, must cost
 * nothing, and is the ONLY spam protection. /migration phase 8 specifies exactly this. */
.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 (1,2,1) and it is `!important`. A hide written as a plain
 * `.mg-only-t { display:none !important }` is (0,1,0), so with `!important` on both
 * sides 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 (gotcha 70).
 */

/* NO BLOG ON THIS SITE: /blog and /blog-search 404 on live, no served document carries a
 * blog widget, and live's feed declares 0 items (tools/_pages.mjs, MEASURED NO BLOG), so the
 * builder stamps nothing with mg-blog-hidden today. The rule is KEPT because runtime.js's
 * blogPager reads and toggles that class, and a class the shipped code can set must have a
 * rule in a shipped sheet (tools/check-invented-classes.mjs). Same specificity note as below. */
#dm .mg-blog-hidden.mg-blog-hidden.mg-blog-hidden {
  display: none !important;
}

/* ---------------------------------------------------------------------------
 * PER-DEVICE CONTENT FORKS.
 *
 * THIS SITE IS DUDA **FLEX**, AND ON FLEX THE CHROME DOES NOT FORK — GOTCHA 5 IS
 * INVERTED. Measured over generated/served-by-device (39 documents, 13 paths x 3
 * device UAs, all 200) and the 39 captures (generated/census-structure.txt):
 *
 *   dmtemplateid    FlexHeader on ALL THREE BANDS, all 13 pages
 *   <body> class    `dmRoot fix-mobile-scrolling flex-site dmResellerSite runtime-module-container`,
 *                   identical on all three bands, all 13 pages
 *   data-flex-site  present on all three bands, all 13 pages
 *
 * `fix-mobile-scrolling` in that class list is LOAD-BEARING (gotcha 35): Duda's
 * body{overflow:hidden} is undone only by it.
 *
 * THE CONTENT FORK IS THREE-WAY, measured on the served BYTES (tools/measure-served-fork.mjs,
 * generated/served-fork.json): desktop == tablet on 0 of 13, desktop == mobile on 0 of 13,
 * each disagreement re-sampled. So each band's body is emitted whole and the two inactive
 * copies are PRUNED pre-paint by the boot script (band_prune). The mg-only-* gates below
 * are the CSS backstop for the interval between parse and prune, and for any element the
 * builder stamps per band rather than per body.
 *
 * THE THREE BANDS DECLARE THREE DIFFERENT VIEWPORTS (13/13 on each band):
 *   desktop  initial-scale=1, minimum-scale=1, maximum-scale=5, viewport-fit=cover
 *   tablet   width=960px                 <- gotcha 3's 960 canvas DOES apply, measured
 *   mobile   width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=5,
 *            viewport-fit=cover
 *
 * THE BAND BOUNDARIES ARE DUDA'S OWN: the ported cascade's media queries are
 * max-width:767px / min-width:768px / max-width:1024px / min-width:1025px, so 1025 is the
 * desktop edge on Duda's own declarations. Only the INACTIVE bands are hidden, inside the
 * media queries; the active band keeps whatever `display` the ported cascade gives it.
 * ------------------------------------------------------------------------- */
@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;
  }
}

/* ---------------------------------------------------------------------------
 * SLIDERS: TWO ssrimageslider WIDGETS, BOTH ON THE HOME PAGE (1 of 13 pages;
 * generated/slider-config.json): #754442902 (2 slots, autoPagination 7s) and #1959847829
 * (1 slot, so its declared autoplay has nothing to advance to). Driven by runtime.js; no
 * rule here. .flexslider 0, data-gallery-bg 0, <video> 0.
 * ------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
 * ACCORDION OPEN STATE — recovering a rule the live CSS read cannot see.
 *
 * THIS SITE, from generated/accordion-config.json (derived from live's own
 * initiateWidget({"type":"SSR_ACCORDION"}) payloads) and corroborated against an
 * `ssraccordion` census of the raw served html:
 *
 *     10 of 13 served documents carry an accordion — home and every service/area page;
 *     12 distinct widget ids, several pages carrying TWO (so the reveal is measured and
 *     stamped PER WIDGET, gotcha 75), and some ids shared across pages; 3 or 5 items each
 *     EVERY widget: firstExpanded FALSE, closeOthers TRUE, LAYOUT_1
 *     live reveal MEASURED per widget: tools/measure-accordion-reveal.mjs ->
 *     generated/accordion-reveal.json
 *
 * So every accordion ships CLOSED, exactly as live serves it, and there is no
 * expandFirstItem height to reproduce. The per-widget stamp is still used, because a
 * site-wide rule that is right today is the shape that ships unnoticed the day one widget
 * differs.
 *
 * 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; the CLOSED rule (max-height:0)
 * is served once per instance and NO open-state rule is served anywhere, because Duda
 * mints one per distinct pixel height at runtime. runtime.js sets an inline max-height from
 * each panel's own scrollHeight; this rule is the ceiling that lets the transition run.
 * ------------------------------------------------------------------------- */
#dm .mg-acc-open.mg-acc-open.mg-acc-open {
  max-height: 2000px;
}

/* NO POPUP HOST RULES. The FLEX click-popup hosts (#flex-popup-overlay,
 * #flex-runtime-popup-container) that a sibling repo had to hand-write have no subject
 * here: link_type="popup" 0, popup_target 0, flex-runtime-popup 0, #dmPopup 0 on all 39
 * served documents. runtime.js's flexPopup() returns on zero triggers, so the rules are
 * omitted rather than shipped inert. */

/* ---------------------------------------------------------------------------
 * PRE-LAUNCH REVIEW, 2026-10-06. Everything below was added by the review.
 * ------------------------------------------------------------------------- */

/* Tablet/phone drawer sub-menus ("Services", "Service Areas" and the nested
 * "Aurora Roofing Florida"). runtime.js drawer() opens and closes each from its caret
 * (or its label, where the label is only "#"); closed is the class gy-sub-closed. The
 * closed state collapses height, padding, border and opacity together (max-height alone
 * clips only the content box), takes the panel out of the tab order, and is !important
 * at doubled id specificity so it outranks the ported cascade's own :hover opener, which
 * a tap leaves stuck on a touch screen and which would otherwise keep a closed group
 * open. */
#hamburger-drawer#hamburger-drawer li.gy-sub > .unifiednav__container_sub-nav {
  overflow: hidden;
  transition: max-height .3s ease, opacity .2s ease;
}
#hamburger-drawer#hamburger-drawer li.gy-sub.gy-sub-closed > .unifiednav__container_sub-nav {
  max-height: 0 !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  border-width: 0 !important;
  opacity: 0 !important;
  pointer-events: none !important;
  visibility: hidden;
}
#hamburger-drawer#hamburger-drawer li.gy-sub:not(.gy-sub-closed) > .unifiednav__container_sub-nav {
  opacity: 1 !important;
  pointer-events: auto !important;
}
/* Closed, the caret points down whatever :hover says. */
#hamburger-drawer#hamburger-drawer li.gy-sub.gy-sub-closed > a .icon::before {
  transform: none !important;
}
/* The close (X) follows the panel's scroll (runtime.js drawer()). Its row and the
 * menu's row can each be their own stacking context, and the later one, the menu,
 * would paint over the X once an open group scrolled up under it. */
#hamburger-drawer .hamburgerButton {
  position: relative;
  z-index: 5;
}
#hamburger-drawer div:has(.hamburgerButton) {
  z-index: 5;
}

/* The 404 page. Duda styles its "4 04" block from a platform sheet that is not part of
 * the port, so the page shipped as four lines of unstyled text pressed against the
 * left edge. Live's measured look (1440 and 390), restated: 120px above, centred on
 * white, a 130px Oswald "4 04", a 22px line with 45px around it, and a 184x37 grey
 * button. */
#dm #error-container {
  padding: 120px 0 0;
  text-align: center;
  background-color: rgb(255, 255, 255);
}
#dm #error-container .four-o-four {
  font-family: Oswald, sans-serif;
  font-size: 130px;
  line-height: normal;
  color: rgb(45, 46, 50);
}
#dm #error-container .four-o-four .lean {
  display: inline-block;
  transform: rotate(15deg);
}
#dm #error-container .description {
  margin: 45px 0;
  font-size: 22px;
  line-height: normal;
  color: rgb(45, 46, 50);
}
#dm #error-container a.back-button {
  display: inline-block;
  box-sizing: border-box;
  min-width: 184px;
  height: 37px;
  margin: 0 0 20px;
  padding: 0 20px;
  border-radius: 2px;
  background-color: rgb(137, 137, 137);
  color: rgb(255, 255, 255) !important;
  font-size: 16px;
  line-height: 37px;
  text-decoration: none !important;
}
#dm #error-container a.back-button:hover,
#dm #error-container a.back-button:focus-visible {
  background-color: rgb(45, 46, 50);
}

/* The 404's header. The header is transparent with white ink until the page scrolls,
 * which works over every other page's hero photograph; the 404 has no hero, so the logo
 * and menu were white on white (on live too) and only the yellow call button showed.
 * Here it takes the navy it has once scrolled. */
body:has(#error-container) #flex-header {
  background-color: rgb(20, 50, 113) !important;
}

/* The contact form's two outcomes (runtime.js forms()).
 * Sent: the section shows only its thank-you panel. Its three widgets are stacked and
 * centred, drawing first, then "We got it.", then the line of copy, with room above and
 * below so it does not sit pressed under the header.
 * Not sent: the error step opened BESIDE the form as a second flex column, which on a
 * phone squeezed the fields into a strip a few words wide. It opens below the form. */
#dm .gy-form-success {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  min-height: 420px;
  padding: 80px 20px;
  text-align: center;
  box-sizing: border-box;
}
#dm .gy-form-success > .graphicWidget {
  order: -2;
  width: 120px;
  height: 120px;
}
#dm .gy-form-success > .graphicWidget svg {
  width: 100%;
  height: 100%;
}
#dm .gy-form-success > .dmNewParagraph:has(h1, h2, h3) {
  order: -1;
}
#dm .gy-form-success > .dmNewParagraph {
  width: 100%;
  max-width: 640px;
}
#dm form.form-widget:has(> [data-error-step]:not(.hidden-step)) {
  flex-direction: column;
}
#dm form.form-widget > [data-error-step]:not(.hidden-step) {
  width: 100%;
  align-items: center;
  padding-top: 32px;
}
