/* 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;
}


/* ---------------------------------------------------------------------------
 * /facesheet's PASSWORD GATE — RE-ASSERT ITS OWN DECLARATIONS AT A SPECIFICITY
 * THE SITE CASCADE CANNOT OUTRANK.
 *
 * WHY THIS EXISTS. Live serves the gate as a STANDALONE DOCUMENT that loads one
 * stylesheet (Duda's font package) and nothing else, so the gate's inline <style>
 * wins uncontested. /facesheet now renders inside the site shell, so that same
 * markup is suddenly competing with the whole ported cascade — and losing.
 *
 * MEASURED, not guessed. CDP CSS.getMatchedStylesForNode on the built page named
 * the winners, and NOT ONE of them is !important — they win on specificity alone:
 *
 *     #dm div.dmContent h1   (0,1,1,2)   beats   #passwordProtectedPopupNonAjax h1   (0,1,0,1)
 *
 * The visible result at 1440, gate vs live's own gate, before this block:
 *     h1     50px Montserrat rgb(42,73,103)   live: 22px Helvetica rgb(66,66,66)
 *     h3     25px Montserrat rgb(0,0,0)       live: 16px Helvetica rgb(66,66,66)
 *     input  16px, height 40px                live: 13.33px, height auto
 * and because .popup-inner is an inline-block that shrink-wraps its widest child,
 * a 50px h1 widened the whole stack from 318px to 505px — past the panel's own
 * 500px box — and pushed the input and both buttons 154px BELOW the panel's
 * bottom edge. The gate rendered as a broken box, on every band.
 *
 * NOTHING HERE IS INVENTED. Every declaration below is COPIED FROM THE GATE'S OWN
 * <style>, which ships verbatim in the page and is live's byte-for-byte. The only
 * change is the `#dm ` prefix that lets them win. The two inputs are `revert`
 * rather than a number because the gate declares no font for them at all — live's
 * 13.3333px IS the user-agent default, and reverting reproduces it without
 * freezing a browser's magic number into this sheet.
 *
 * !important is deliberately NOT used: nothing being overridden here uses it, and
 * adding it would break the gate's own media query at 420px, which legitimately
 * restyles the panel on phones.
 * ------------------------------------------------------------------------- */
#dm #passwordProtectedPopupNonAjax h1 {
  font: 22px Helvetica, Arial;
  color: #424242;
  margin-bottom: 5px;
}

#dm #pdw-wrapper h3 {
  font: 16px Helvetica, Arial;
  color: #424242;
  margin-top: 7px;
}

#dm #pdw-wrapper input[type="password"],
#dm #pdw-wrapper input[type="button"] {
  font: revert;
}

#dm #pdw-wrapper input[type="password"] {
  height: auto;
}


/* ---------------------------------------------------------------------------
 * LIGHTBOX — undo the three PhotoSwipe base-CSS defaults that only PhotoSwipe's
 * own JS ever overrides. Scoped to `.mg-pswp-on`, which runtime.js adds on open
 * and removes on close, so the resting document is byte-for-byte unchanged and
 * the pixel gate still sees what it saw.
 *
 * MEASURED on the built home page at 1440, clicking a gallery thumbnail after
 * scrolling to the gallery (scrollY 3567):
 *
 *     .pswp rect top   -3567   (position:absolute -> anchored to the DOCUMENT)
 *     .pswp__bg opacity     0
 *     .pswp__ui        --hidden  -> close/counter/arrows at opacity .001
 *
 * i.e. the overlay opened a full screen ABOVE the visitor, transparent, with
 * invisible controls. Live, same click: fixed, top 0, bg opacity 1, ui --fit,
 * arrows .75, counter "1 / 23".
 *
 * Repeated behind #dm and on the class itself to clear Duda's per-widget
 * `!important` rules (gotcha 70). The base sheet's own `z-index:...!important`
 * on `.pswp` is left alone — it is already above everything.
 * ------------------------------------------------------------------------- */
.pswp.mg-pswp-on.mg-pswp-on {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.pswp.mg-pswp-on .pswp__bg {
  opacity: 1;
}

/* The image is placed by PhotoSwipe's JS with a transform per slide; this viewer
 * has no slide geometry, so it centres with flow instead. `position:absolute` +
 * `inset:0` + `margin:auto` is what keeps it centred rather than pinned to the
 * top-left corner the base `.pswp__img{position:absolute;top:0;left:0}` gives. */
.pswp .mg-pswp-img {
  position: absolute;
  inset: 0;
  margin: auto;
  display: block;
  max-width: 92vw;
  max-height: 88vh;
  object-fit: contain;
}

/* The page behind an open lightbox must not scroll — live's PhotoSwipe does this
 * from its own JS via a class on <html>. */
html.mg-pswp-lock,
html.mg-pswp-lock body {
  overflow: hidden;
}


/* ---------------------------------------------------------------------------
 * DRAWER SUBMENUS — collapsed by default, opened by the caret.
 *
 * WHAT SHIPPED. Both groups stood fully expanded inside the drawer at every
 * width, on the build AND on live: measured at 375 with a phone UA, the two
 * `ul.unifiednav__container_sub-nav` elements were 250px and 200px tall with
 * `max-height: none` and `overflow: visible`, and tapping the caret changed
 * neither. So the drawer opened onto a 450px wall of sub-pages and the carets
 * were decoration.
 *
 * The old handler could not have fixed it. It toggled `aria-expanded` on the
 * <li> — which no rule here reads — and it only intercepted the click when the
 * parent's href was `#` or empty, which is true of "Service Areas" and NOT of
 * "Applications" (`/#Applications`). That is the shape the workflow warns about:
 * the one parent that was already inert gets a handler, the one that navigates
 * away keeps navigating, and the fix looks done while changing nothing.
 *
 * House standard, which overrides what live does: EVERY group starts closed,
 * including the group containing the current page. The caret toggles; the label
 * still navigates wherever it points somewhere real.
 *
 * TWO THINGS MEASURED HERE THAT DECIDE THE IMPLEMENTATION:
 *   1. `.icon` already carries `transform: scale(1.3)` and `.icon::before`
 *      already carries `rotate(180deg)`. Flipping the caret by writing a
 *      transform on `.icon` silently loses to the scale it already has, so the
 *      rotation goes on `::before`, which is where the 180 already lives.
 *   2. The caret is an inline <span> INSIDE the label's anchor. Giving it a 44px
 *      min-width/min-height to make a tap target turns it into a box wide enough
 *      to wrap the label, leaving carets floating above and right of their text.
 *      An invisible `::after` overlay gives it a real touch area at no layout
 *      cost.
 *
 * Scoped to `.layout-drawer`, so the desktop nav is untouched.
 * ------------------------------------------------------------------------- */
.layout-drawer ul.unifiednav__container_sub-nav {
  max-height: 0 !important;
  overflow: hidden !important;
  /* max-height clips the CONTENT box, so the list's own padding and border
   * survive a collapse to 0 and leave a visible sliver under every parent.
   * Collapse them with it. This is `!important` because Duda's own
   * `[data-auto="sub-pages"]` padding rule is, and the collapsed state has to be
   * able to answer it at any specificity. */
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  border-top-width: 0 !important;
  border-bottom-width: 0 !important;
  transition: max-height 0.3s ease-out;
}

.layout-drawer li.mg-sub-open > ul.unifiednav__container_sub-nav {
  /* The open height is written inline from the list's measured scrollHeight —
   * see runtime.js. This only has to stop the collapsed rule from winning. */
  max-height: none;
}

/* The caret's touch area, supplied without changing its inline footprint. */
.layout-drawer a.unifiednav__item .icon.mg-caret {
  position: relative;
}
.layout-drawer a.unifiednav__item .icon.mg-caret::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}
/* Rotate ::before, NOT .icon — .icon's scale(1.3) would win, and the 180deg the
 * caret already renders with lives on ::before too.
 *
 * `!important` because the 180 comes from one of Duda's longest selectors —
 * `#dm .main-navigation.unifiednav[data-nav-structure="VERTICAL"]...
 *  .unifiednav__item_has-sub-nav > .nav-item-text .icon::before` — which a plain
 * class rule cannot outrank. Measured before adding it: the open group's caret
 * still computed matrix(-1,0,0,-1), i.e. it never flipped. */
.layout-drawer .icon.mg-caret::before {
  transition: transform 0.3s ease-out;
}
.layout-drawer li.mg-sub-open > a.unifiednav__item .nav-item-text .icon.mg-caret::before {
  transform: rotate(0deg) !important;
}


/* ---------------------------------------------------------------------------
 * LEGAL PAGES — /privacy-policy and /terms-and-conditions.
 *
 * They borrow /cosigner-application's ported shell, so the heading rhythm they
 * inherit is whatever the Duda cascade gives an inner page. MEASURED on the first
 * build at 1440: h2 already sat 29px below the block above it and lists already
 * had 16px either side — but CONSECUTIVE PARAGRAPHS measured a 0px gap, because
 * Duda's reset zeroes `margin` on `p`. The effective-date line therefore sat flush
 * against the paragraph under it and the page read as one slab in places.
 *
 * Only the gap that actually measured 0 is set here; the rest is left as the
 * cascade already renders it rather than re-declaring values that are correct.
 * ------------------------------------------------------------------------- */
.mg-legal p + p {
  margin-top: 16px;
}

.mg-legal ul {
  padding-left: 24px;
}

.mg-legal li + li {
  margin-top: 4px;
}


/* ---------------------------------------------------------------------------
 * FOOTER LEGAL LINKS.
 *
 * A SIBLING OF `.copyright`, NOT A CHILD. Putting them inside it looked tidier —
 * the links would inherit the block's alignment for free — but `.copyright` is
 * `display: flex; flex-wrap: nowrap; flex-direction: row`, so a third child became
 * a third COLUMN: measured at 1440 the links sat at x 399 on the copyright's own
 * row, and at 390 they were squeezed into a 157px box against 187px of text and
 * wrapped. Making the container wrap fixed that but changed the copyright text's
 * own layout on the phone band (its first item went from 169x52 to 231x26), which
 * is a live-matched block this review has no business moving. Outside it, the
 * copyright is byte-for-byte what it was.
 *
 * ALIGNMENT IS SCOPED ON THE BAND, NOT ON A WIDTH MEDIA QUERY, because the markup
 * already states the boundary: the two footers carry `data-mg-band="dt"` and
 * `data-mg-band="m"`. Measured on the copyright block itself, `text-align` is
 * `left` at 1440 and 768 (both the `dt` band) and `center` at 375 (`m`), so the
 * links follow that per band rather than taking one value everywhere.
 *
 * THE COLOUR IS SET EXPLICITLY AND IS NOT INHERITED. The footer ground is
 * rgb(42,73,103) and the copyright text is white, but an <a> does not inherit
 * that: the site's link colour resolves to the page's dark body ink, which would
 * ship these as near-black on dark blue.
 * ------------------------------------------------------------------------- */
.mg-legal-links {
  margin-top: 8px;
  font-size: 13px;
  line-height: 1.4;
}

[data-mg-band="dt"] .mg-legal-links {
  text-align: left;
}

[data-mg-band="m"] .mg-legal-links {
  text-align: center;
}

.mg-legal-links a,
.mg-legal-links a:link,
.mg-legal-links a:visited {
  color: #fff;
  text-decoration: underline;
}

.mg-legal-links a:hover,
.mg-legal-links a:focus {
  color: #deac3d;   /* the site's accent, as used on the pager's active pill */
}

.mg-legal-links .mg-legal-sep {
  color: #fff;
  opacity: 0.6;
}


/* ---------------------------------------------------------------------------
 * CONTACT-FORM LEGAL LINKS — one line, centred, white, clearing the submit.
 *
 * THE INK IS WHITE BECAUSE THE FORM SITS ON A PHOTOGRAPH, NOT ON PAPER. The
 * contact row is `.dmRespRow.u_ContactUs.hasBackgroundOverlay`, whose ::before
 * lays rgb(42,73,103) over a background image, and every label in that form —
 * FULL NAME, EMAIL ADDRESS, PHONE NUMBER, MESSAGE — computes rgb(255,255,255).
 *
 * THIS LINE FIRST SHIPPED IN rgb(42,73,103), WHICH IS THE OVERLAY'S OWN COLOUR —
 * navy on navy, all but invisible on screen. The check that let it through walked
 * the ancestors for a background COLOUR, found none (nothing in the chain sets
 * one; the row paints with a background IMAGE plus that ::before overlay), fell
 * back to assuming white, and scored the pair 9.34:1 against a ground that does
 * not exist. A fallback inside a contrast check is a fabricated measurement.
 * Where no ancestor declares a colour, read the SIBLING text instead — here the
 * form's own labels — or read the pixels.
 *
 * CENTRED to sit under the centred SUBMIT button that ends the form, rather than
 * hanging off the left edge below it.
 *
 * `clear: both` IS LOAD-BEARING, and source order is not enough on its own. Duda
 * wraps the submit in `.dmformsubmit.dmWidget.R`, which is FLOATED, so a block
 * that follows it in the markup flows up ALONGSIDE it and lands beside — or
 * above — the button it is supposed to sit under.
 *
 * The space above is PADDING, not margin. A margin here collapses against the
 * submit widget's own box and buys nothing: the declared value simply does not
 * appear in the rendered gap.
 *
 * ONE LINE, NOT A SENTENCE — two link labels on a single row, so it reads as a
 * form footnote rather than stray body copy.
 * ------------------------------------------------------------------------- */
.mg-form-legal {
  clear: both;
  padding-top: 15px;
  font-size: 13px;
  line-height: 1.4;
  text-align: center;
}

.mg-form-legal a,
.mg-form-legal a:link,
.mg-form-legal a:visited {
  color: #fff;
  text-decoration: underline;
}

.mg-form-legal a:hover,
.mg-form-legal a:focus {
  color: #deac3d;
}

.mg-form-legal .mg-legal-sep {
  color: #fff;
  opacity: 0.6;
}


/* ---------------------------------------------------------------------------
 * H1 ON THE THREE APPLICATION PAGES.
 *
 * /cosigner-application, /defendant-application and /owa-form each ship as a bare
 * JotForm iframe with ZERO visible text — measured, their rendered body contains
 * no heading of any level and no copy at all, and each carries 0 <h1>. Live is the
 * same, so this is inherited rather than lost in the port, but "exactly one H1 per
 * page" is the standard and a page whose only content is a third-party iframe is
 * exactly the case where the heading is doing real work: it is the only thing on
 * the page that names it to a reader or to a crawler.
 *
 * DELIBERATE DEVIATION FROM LIVE — flagged in the review, not slipped in.
 * ------------------------------------------------------------------------- */
.mg-page-h1 {
  padding: 24px 0 8px;
}

.mg-page-h1 h1 {
  margin: 0;
}


/* Blog search results page. */
.mg-search-count {
  font-size: 20px;
  margin-top: 8px;
}

.mg-search-empty {
  text-align: center;
  padding: 24px 0 48px;
}


/* A slide with no caption gets PhotoSwipe's own `--fake` marker, and the bar is
 * hidden rather than left as an empty strip. Live carries `--fake` on all 23
 * slides of the home gallery, with `.pswp__caption__center` empty on every one. */
.pswp .pswp__caption.pswp__caption--fake {
  display: none;
}
