/* Hand-written overrides for this migration (copied forward from an earlier migration, 2026-10-01; no previous client is named in a shipped file).
 *
 * 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 MUST BE CENSUSED ON THIS SITE. That sentence is here because this
 * file has now arrived describing the wrong repo TWICE in this chain — once from two
 * generations back, whose structural section described the CLASSIC Duda template
 * (StandardLayoutMultiD / Hamburger / mobileHamburgerLayout) on a site that is FLEX, and
 * again into this repo carrying the previous FLEX site's own measurements. A number in a
 * hand-written sheet is exactly the residue gotcha 63 says survives a copy still looking
 * correct, so every figure below is re-derived here rather than inherited.
 * The RULES were sound and are kept; every measurement around them is re-taken.
 *
 * Population, CENSUSED ON THIS SITE 2026-10-01 (the figures that arrived here were the
 * previous site's, again):
 *
 *   pages.txt                 56 paths
 *   generated/blog.rss        0 <item>s — this site has NO blog (no widget, no export feed)
 *   served documents          168 (56 paths x 3 bands), plus the 404 on each band
 *
 * A reader who checks ONE figure and finds it right trusts the rest, so a partially
 * re-censused header is more dangerous than a wholly foreign one.
 *
 * 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 either of this site's two
 * forms, so this is ours and must cost nothing.
 *
 * /migration phase 8 specifies exactly this and one fleet repo shipped the spec
 * unimplemented — its honeypot rendered as an ordinary 40px text field and read as a
 * 46px layout defect. */
.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.
 */

/* ---------------------------------------------------------------------------
 * BLOG PAGINATION — NO SUBJECT ON THIS SITE.
 *
 * This site has no blog: no blog widget in any of the 168 served documents
 * (data-paginate-total-elements 0, internal_blog_list 0), no blog.rss in the export, and
 * a live feed with 0 items. No element is ever stamped mg-blog-hidden, so the two rules
 * below match nothing here. They are kept because they are inert without a subject and
 * the next site copied from this one may have a blog — the reasoning lives in the
 * process doc (gotchas 69 and 112), not in figures from this file.
 * ------------------------------------------------------------------------- */
#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 — the signature of one
 * shared element rather than a per-page fault.
 *
 * NO SUBJECT ON THIS SITE: it has no blog, so no card is ever hidden and this rule
 * matches nothing. The mechanism is recorded because it is easy to get wrong where a
 * blog exists — on the site this file was measured on, the shipped hidden cards gave the
 * last visible card a +30px `padding-bottom` (Duda's
 * `[list-layout=recent_posts] .postArticle:not(:last-child)` rule), a constant dH on
 * every post.
 *
 * WHY THE OVERRIDE NEEDS !important (reasoning, not a measurement of this site):
 * a matching override can still LOSE THE SPECIFICITY FIGHT, which is the failure that
 * looks like the rule not being there. Counting:
 *
 *   ours   #dm + .postArticle + :not(.mg-blog-hidden) + :has(.postArticle.mg-blog-hidden)
 *          = (1,4,0)   — :has() takes the specificity of its most specific argument
 *   Duda's #dm + 3 attribute selectors + .postArticle + :not(:last-child)
 *          = (1,5,0)   — and it carries NO !important
 *
 * (1,5,0) beats (1,4,0), so the override was outranked and silently did nothing.
 * `!important` is what settles it, because Duda's rule here is not !important — this
 * is gotcha 70's specificity trap and the "a pixel count identical across runs means
 * the fix never took effect" rule arriving together.
 *
 * 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. */
#dm .postArticle:not(.mg-blog-hidden):has(+ .postArticle.mg-blog-hidden) {
  padding-bottom: 0 !important;
}

/* ---------------------------------------------------------------------------
 * PER-DEVICE WIDGET FORKS.
 *
 * THIS SITE IS DUDA **FLEX**, AND ON FLEX THE CHROME DOES NOT FORK — GOTCHA 5 IS
 * INVERTED. Measured over generated/served-by-device (168 documents, 56 paths x 3
 * device UAs, the 404 excluded):
 *
 *   dmtemplateid    FlexHeader on ALL THREE BANDS, all 56 pages
 *   <body> class    `dmRoot fix-mobile-scrolling flex-site dmResellerSite`,
 *                   BYTE-IDENTICAL on all three bands, all 56 pages
 *   data-flex-site  present on all three bands, all 56 pages
 *
 * So dmtemplateid CANNOT discriminate the fork here, and gotcha 48/88's
 * dmDesktopBody / dmTabletBody / dmMobileBody classes DO NOT EXIST on this platform.
 *
 * THE CONTENT FORK IS STILL THREE-WAY, measured on the served BYTES: desktop == tablet
 * on 0 of 56, desktop == mobile on 0 of 56, tablet == mobile on 0 of 56, and tag counts
 * differ across bands on 56 of 56 (generated/served-fork.json: THREE-WAY).
 *
 * THE TABLET DECLARES ITS OWN CANVAS. <meta name="viewport" content="width=960px"> on
 * 56 of 56 tablet documents, against device-width on desktop and mobile, so gotcha 3's
 * 960 canvas applies here — measured, not carried.
 *
 * THE `mg-only-t` GATE THEREFORE CARRIES A CONTENT FORK, NOT A CHROME FORK. The header,
 * drawer and overlay are one shape on every band; what differs per band is the page
 * content.
 *
 * THE BAND BOUNDARIES ARE DUDA'S OWN, COUNTED ON THIS SITE over all 858 sheets in
 * generated/live/css:
 *
 *     max-width: 767px   x285      min-width: 768px   x208
 *     max-width: 1024px  x217      min-width: 1025px  x53
 *     min-width: 0px     x33
 *
 * 1025 is therefore the desktop edge on Duda's own declarations rather than by assuming
 * 1024+1. The drawer behaviour across widths was verified against live by the shared
 * check-drawer (105 ok over 9 band readings, generated/check-drawer.log); mind the UA
 * trap (gotcha 66) before re-taking any edge: Duda picks its document by USER-AGENT.
 *
 * So, on (a) alone: 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 — hiding
 * all three and restoring one with `display: revert` reverts past the author cascade
 * to the UA default and replaces 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;
  }
}

/* ---------------------------------------------------------------------------
 * SLIDERS: ONE WIDGET ON ONE PAGE, AND IT DOES MOVE. NO RULE IS NEEDED HERE.
 *
 * Censused on this site: `ssrimageslider` occurs on exactly ONE page, the home page, and
 * tools/derive-slider-config.mjs (56 pages read) finds ONE widget, declaring
 *
 *     autoPlay=true   interval=10s   pauseOnHover=true   animation=slide
 *
 * read from the widget's own payload (gotcha 90), never from runtime.js's constant.
 * flexslider 0. The shared check-slider asserted the build against live's start state
 * and motion, 15 ok (generated/check-slider.log). The gate did not hide the region: the
 * home page scored 0.000% at all three widths in gate run 2.
 * ------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
 * ACCORDION OPEN STATE — recovering a rule the live CSS read cannot see.
 *
 * CENSUSED ON THIS SITE: ONE accordion widget (903388023), on /faq only, 10 items,
 * firstExpanded FALSE, closeOthers TRUE, LAYOUT_1 (generated/accordion-config.json, read
 * from live's own initiateWidget payload). check-accordion drove it against live at
 * 1440 / 768 / 375 and passed each (generated/check-accordion-*.log).
 *
 * 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 over the
 * 168 served documents: 6 <style data-styled> blocks (the /faq page on three bands),
 * 3 closed `max-height:0` declarations, and ZERO non-zero max-height declarations —
 * the open-state rule is not served anywhere.
 *
 * THE OPEN CLASS IS MINTED PER DISTINCT PIXEL HEIGHT, so items sharing a height share
 * a class. 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.
 * ------------------------------------------------------------------------- */
#dm .mg-acc-open.mg-acc-open.mg-acc-open {
  max-height: 2000px;
}

/* ---- Pre-launch review 2026-09-29 ------------------------------------------ */

/* Legal pages (tools/build-legal-pages.mjs). Duda's reset zeroes margin on p, ul
 * and h2, so the template text would otherwise render as one slab with every
 * heading pressed against the paragraph above it. */
#dm .gy-legal-row {
  max-width: 1200px;
  margin: 0 auto;
  padding: 60px 40px 80px;
  box-sizing: border-box;
}
#dm .gy-legal-row h2 {
  margin: 36px 0 12px;
}
#dm .gy-legal-row p {
  margin: 0 0 14px;
}
#dm .gy-legal-row ul {
  margin: 0 0 14px;
  padding-left: 1.4em;
  list-style: disc;
}
#dm .gy-legal-row li {
  margin: 0 0 4px;
}
@media (max-width: 767px) {
  #dm .gy-legal-row {
    padding: 40px 16px 60px;
  }
}

/* ---- FLEX popup overlay and container (runtime.js flexPopup) ------------------------
 * Duda's runtime BUILDS these two elements when the popup trigger is clicked, so no
 * capture contains them and port-css has nothing to port. The values are
 * an earlier FLEX migration's, read off live there with a real hit-tested click; on THIS
 * site they are asserted against live by tools/check-flex-popup.mjs (overlay colour,
 * container, dialog box and form box at 1440). The dialog's own rules arrive through
 * the ported cascade of /contact-useab4f43e.
 */
#flex-popup-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  background-color: rgba(0, 0, 0, 0.533);
}
#flex-runtime-popup-container {
  position: fixed;
  inset: 0;
  z-index: 201;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: transparent;
}

/* ---- Pre-launch review 2026-10-08 ------------------------------------------ */

/* Tablet/phone drawer sub-menus ("About Us", "Painting", "Other Services", "Service
 * Areas"). runtime.js drawer() opens and closes each from its caret; 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. */
#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;
}
/* 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),
#hamburger-drawer div:has(.hamburgerButton) {
  z-index: 5;
}

/* /painting-services/service-areas: the 30 town buttons are fixed-width pills (three
 * across at 229px in a 768px window, 293px on an iPad's 960px canvas; one per row at
 * 290px on a 320px phone) and the longest label, "Litchfield Spray-Applied Exterior
 * Painting", needs 308px at the 16px it is set in, so 12 labels broke onto two lines
 * at 768, 3 on an iPad and 3 at 320 (on live too). The type scales with the pill
 * instead, all 30 alike so a row never mixes sizes; 16px again from 1025 and from
 * 356px phones up. */
@media (min-width: 768px) and (max-width: 1024px) {
  body[data-page-alias="painting-services/service-areas"] .dmBody a.dmButtonLink[href^="/areas-of-service/"] .text {
    font-size: clamp(11.5px, 1.5vw, 15px) !important;
    white-space: nowrap;
  }
}
@media (max-width: 355px) {
  body[data-page-alias="painting-services/service-areas"] .dmBody a.dmButtonLink[href^="/areas-of-service/"] .text {
    font-size: 4.5vw !important;
    white-space: nowrap;
  }
}

/* The "Contact Us" button added after the /faq accordion and the /gallery photos
 * (tools/review-content-fixes.py; neither page had a call to action). It is the
 * site's own button, restated from its measured look on the service pages (280x60
 * pill, 50px on phones where it spans the column; rgb(155,17,19), white 16px/700
 * Roboto, no icon), since Duda styles each button by a per-page id rule a new element
 * cannot borrow. FLEX groups place children by `order`, so it is sent to the end of
 * its column explicitly. */
#dm#dm .gy-cta-wrap {
  order: 999;
  width: 100%;
  height: auto;
  min-height: 0;
  display: flex;
  justify-content: center;
  margin: 40px 0 0;
  padding: 0;
  position: relative;
  z-index: 2;   /* above the section's decorative brush stroke (/faq, phones) */
}
#dm#dm a.gy-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 280px;
  max-width: 100%;
  height: 60px;
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  border: 0;
  border-radius: 50px;
  background: rgb(155, 17, 19);
  transition: background-color .2s, border-color .2s;
  text-decoration: none;
}
#dm#dm a.gy-cta .text {
  color: rgb(255, 255, 255);
  font-family: Roboto, "Roboto Fallback", sans-serif;
  font-size: 16px;
  font-weight: 700;
  white-space: nowrap;
}
#dm#dm a.gy-cta .iconBg {
  display: none;
}
@media (max-width: 767px) {
  #dm#dm a.gy-cta {
    width: min(100%, calc(100vw - 30px));
    height: 50px;
  }
}
/* /faq: the accordion's group is a ROW with the accordion as its only child, so a
 * second child became a second column and halved the accordion. The row wraps, and
 * each of the two takes a full line. */
body[data-page-alias="faq"] #dm#dm #group_951 {
  flex-wrap: wrap;
}
body[data-page-alias="faq"] #dm#dm #group_951 > .widget-wrapper {
  flex: 0 0 100%;
  max-width: 100%;
}

/* 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 three lines of unstyled text pressed against the
 * left edge. Live's look, restated: 120px above, centred on white, a 130px Oswald
 * "4 04" with the first 4 leaning 15deg, 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: 130px;
  color: rgb(45, 46, 50);
}
#dm #error-container .four-o-four .lean {
  display: inline-block;
  transform: rotate(15deg) translate(-5px, -4px);
}
#dm #error-container .description {
  margin: 45px 0;
  font-size: 22px;
  line-height: 44px;
  color: rgb(45, 46, 50);
}
#dm #error-container a.back-button {
  display: inline-block;
  box-sizing: border-box;
  width: 184px;
  height: 37px;
  margin: 0 0 20px;
  padding: 0;
  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);
}

/* Desktop header: the menu on ONE row (review 2026-10-08, at the client's request).
 * The eight labels need 814px at full size (16px type, 12px of padding each side) and
 * the menu's column was 734px at every width from 1280 up (621px at 1025), so
 * "Contact" sat alone on a second row, on live too. Three things give the row its
 * room, in this order: the header's container widens from 1200 to 1360px, which moves
 * the logo and the phone button out toward the edges; the menu takes all the space
 * between them; and below 1400px the labels' side padding tightens (12px -> 3px at
 * 1025), with the type easing from 16px to 14px only under 1280px. Measured: one row
 * with at least 10px clear of the logo and the button at 1025-1300, 40px from 1366. */
@media (min-width: 1025px) {
  #dm#dm #flex-header [id="46b8b825"] {
    max-width: 1360px !important;
  }
  #dm#dm #flex-header [id="9f93f799"] > #widget_teq {
    flex: 1 1 auto !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: none !important;
  }
  /* The logo and the phone button keep the size they had in the 1200px container
   * (19.8% and 15.8% of the row, 226px and 180px at most). */
  #dm#dm #flex-header [id="9f93f799"] > :first-child {
    flex: 0 0 auto !important;
    width: min(19.8%, 226px) !important;
  }
  #dm#dm #flex-header [id="9f93f799"] > #widget_35u {
    flex: 0 0 auto !important;
    width: min(15.8%, 180px) !important;
  }
  #dm#dm nav[id="2659548942"] > ul {
    flex-wrap: nowrap !important;
    justify-content: center;
  }
  #dm#dm#dm .dmInner nav[id="2659548942"].main-navigation.unifiednav .unifiednav__container:not([data-depth]) > .unifiednav__item-wrap > .unifiednav__item {
    padding-left: clamp(3px, calc(3px + (100vw - 1025px) * 0.024), 12px) !important;
    padding-right: clamp(3px, calc(3px + (100vw - 1025px) * 0.024), 12px) !important;
    white-space: nowrap;
  }
  #dm#dm#dm .dmInner nav[id="2659548942"].main-navigation.unifiednav .unifiednav__container:not([data-depth]) > .unifiednav__item-wrap > .unifiednav__item .nav-item-text {
    font-size: clamp(14px, calc(14px + (100vw - 1025px) * 0.0078), 16px) !important;
  }
}
