/* 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 THIS SITE, not carried over — the previous repo's comment described
 * an APPEND-style "Show More" control and a 29-post blog, and BOTH are wrong here.
 * Gotcha 83 warns that the withholding control has at least two shapes and that a
 * tool keyed on one reports a clean run on a site using the other.
 *
 * Live's /blog declares data-paginate-total-elements="16" and visible-items="10",
 * and renders exactly 10 distinct data-blog-post-alias values. The control is a
 * NUMBERED PAGER — nav.pagination-nav with <a data-page="N" data-action="paginate">
 * — which REPLACES the card set on each click rather than appending, so the cards
 * are collected as a UNION across pages. There is no .more-posts-text-container and
 * no .morePosts anywhere on this site.
 *
 * blog.rss independently declares 16 items, and the recovered index matched it
 * exactly: 16 cards, 0 missing, 0 extra, on all three bands.
 *
 * SEVENTEEN pages withhold cards, not one. Each of the 16 blog POSTS carries the
 * same widget declaring 15 (every post except itself) and renders 10. A static
 * build has no Duda backend, so every card ships 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. */
#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.
 *
 * MEASURED ON THIS SITE at 1440 on /blog: live's 10th card IS :last-child, takes
 * padding-bottom 0 and measures 317px. Ours is followed by the 6 withheld cards, so
 * it stops matching :last-child, KEEPS 30px and measures 347px — a constant +30px at
 * every width with dTop 0, which is gotcha 69's one-shared-element signature. It cost
 * 4.500% / 2.592% / 2.545% at 375 / 768 / 1440.
 *
 * THE RULE THAT HAS TO BE BEATEN, read out of the ported cascade rather than assumed:
 *
 *   #dm [blog-posts-feature-flag=true][list-layout=recent_posts][posts-padding="15"]
 *       .postArticle:not(:last-child) { padding-bottom: 30px }
 *
 * SPECIFICITY IS THE WHOLE PROBLEM AND THE INHERITED COMMENT HAD IT WRONG. That
 * comment asserted Duda's rule was (0,2,1) and this one (0,4,1), so a plain rule
 * would "win on merit". Duda's actual rule here is ONE ID PLUS THREE ATTRIBUTE
 * SELECTORS PLUS A CLASS PLUS A PSEUDO-CLASS = (1,5,0). The first version of the
 * rule below was
 *     #dm .postArticle:not(.mg-blog-hidden):has(+ .postArticle.mg-blog-hidden)
 * which is (1,4,0) and LOSES — measured in the browser, the selector matched the
 * right card (`matches: true`) and the computed padding-bottom was still 30px. An
 * override that matches and does not apply is invisible without reading the computed
 * value, which is why this was found by measuring and not by reading.
 *
 * Three attribute selectors beat any number of repeated classes only up to a point:
 * repeating `.postArticle` three times takes this to (1,6,0), which clears (1,5,0)
 * without needing !important — and Duda's rule is not !important, so winning on
 * specificity is the honest fix rather than escalating.
 *
 * Keyed on `:has(+ .mg-blog-hidden)` rather than on the stamped `lastArticle` 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.postArticle.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 AGAIN. The paragraph that stood here has now been
 * rewritten on each of several migrations and named pages that did not exist on the
 * site it was describing. None of it was ever a fact about
 * prescottspasandbilliards.com.
 * 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, 123 captures, <style>/<script>
 * stripped first): .flexslider is on 1 of 41 pages — the home page — identically on
 * all three bands. There are ZERO .bgGallerySlide elements on any band, and FOUR
 * photo galleries across 4 pages. That one slider WAS measured before anything was
 * excluded, by watching live for 30s at 1440 with no scrolling: it holds 2 real
 * slides and cross-fades between them every 5s, matching its declared
 * slideshowSpeed:5000. So it IS excluded, and tools/check-slider-motion.mjs is the
 * per-element check that covers what the exclusion removes,
 * 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 26 of 41 pages, identically
 * on all three bands. NOTE a raw count of the data-grab ATTRIBUTE reports 36 per
 * page and 44 on /platinum-spas — gotcha 65 exactly: one widget carries eight
 * different data-grab tokens. The widget count is 1 per page, and 2 on
 * /platinum-spas.
 *
 * 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;
}


/* ---------------------------------------------------------------------------
 * THE OPEN HEIGHT IS NOW DRIVEN INLINE FROM THE PANEL'S MEASURED scrollHeight
 * (runtime.js), so the rule above is only a no-JS fallback. It is kept rather
 * than deleted because a 2000px ceiling still beats 0 if the script never runs —
 * but on its own it is a SNAP, not a transition: with 2000px of declared travel
 * against 58-392px of real content, the panel reaches full height inside the
 * first 25ms of a 300ms ease-out, and closing hangs above the content height for
 * almost the whole duration before dropping. Sampled per animation frame after a
 * real click: 58/58 at 22ms on /hot-tub-disposal, 134/134 at 17ms on
 * /camp-verde-az, 262/262 at 25ms on /platinum-spas.
 * ------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
 * BLOG PAGER — THE PAGE NUMBERS WERE INVISIBLE, and that is a defect separate
 * from the pager being dead.
 *
 * Duda's skin sets `.pagination-link { color: inherit }` and gives the ITEM
 * `color: var(--btn-text-color)` — white — then paints a background on the active
 * item only, via `:has(.pagination-link.active)`. Every OTHER number therefore
 * inherits white onto the white `.dmInner` behind it. Measured on this build at
 * 1440 and 375, on /blog and on a post, and identical on LIVE:
 *     active "1"   white on rgb(0,67,127)   =  9.96:1
 *     "2"          white on white           =  1.00:1
 *     next arrow   white on white           =  1.00:1
 * The visitor sees a single dark "1" and no way to reach the rest.
 *
 * Scoped :not(.active):not(.disabled) so the active pill keeps its white ink and
 * the greyed-out arrow keeps Duda's own #c9cacd. --btn-bg-color is the pill's own
 * colour, so this follows whatever palette a widget is themed with rather than
 * hardcoding one. Live has the same fault; invisible-by-default UI is not a
 * design choice worth reproducing.
 * ------------------------------------------------------------------------- */
#dm .pagination-nav .pagination-list .pagination-item .pagination-link:not(.active):not(.disabled) {
  color: var(--btn-bg-color, #0d560d);
}
#dm .pagination-nav .pagination-list .pagination-item .pagination-link:not(.active):not(.disabled):hover {
  text-decoration: underline;
}

/* ---------------------------------------------------------------------------
 * DRAWER SUBMENUS — the transition, and a real tap target for the caret.
 *
 * runtime.js sets max-height inline from the group's own scrollHeight, so the
 * travel is the real travel and this transition actually runs. overflow:hidden is
 * restated because the collapsed group must clip, not merely be short.
 *
 * THE CARET IS NOT SIZED TO 44px. It is an inline <span> inside the label's
 * anchor, and giving it min-width/min-height makes it a box wide enough to wrap —
 * the carets then float above and to the right of their labels. An invisible
 * ::after overlay gives the touch area while the caret keeps its inline footprint.
 * ------------------------------------------------------------------------- */
#dm .layout-drawer .unifiednav__container_sub-nav {
  overflow: hidden;
  transition: max-height 0.3s ease-out;
}
#dm .layout-drawer .unifiednav__item_has-sub-nav .icon {
  position: relative;
}
#dm .layout-drawer .unifiednav__item_has-sub-nav .icon::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}

/* ---------------------------------------------------------------------------
 * LIGHTBOX — the enlarged photo.
 *
 * PhotoSwipe positions .pswp__img absolutely because its own JS transforms each
 * slide; without that JS the photo pins to a corner. Centre it in the overlay and
 * cap it so a tall image cannot outgrow the viewport. runtime.js supplies the
 * rest of what PhotoSwipe's JS would have done — promoting .pswp to fixed,
 * raising .pswp__bg off opacity 0, and dropping the captured pswp__ui--hidden
 * that holds the close button and both arrows at opacity .001.
 * ------------------------------------------------------------------------- */
#dm ~ .pswp .mg-pswp-img,
.pswp .mg-pswp-img {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  max-width: 90vw;
  max-height: 82vh;
  width: auto;
  height: auto;
  object-fit: contain;
}
.pswp .pswp__caption--fake {
  display: none !important;
}

/* ---------------------------------------------------------------------------
 * LEGAL LINKS ON THE CONTACT FORMS.
 *
 * ONE LINE, NOT A SENTENCE — "Privacy Policy | Terms & Conditions" on a single
 * row. A sentence reads as stray body copy under a form and wraps badly on a
 * phone.
 *
 * clear:both IS THE WHOLE POINT. Duda wraps the submit button in
 * .dmformsubmit.dmWidget.R, which is FLOATED, so a block placed after it in the
 * markup flows up ALONGSIDE it and lands level with — or above — the last field
 * while sitting last in the DOM. Being the form's last child does not put it
 * underneath; clearing the float does.
 *
 * PADDING, NOT MARGIN, for the ~15px above it: a margin collapses against the
 * preceding widget's own box and measures 0 at every width, with nothing in the
 * CSS to hint at it.
 *
 * THE INK IS LIGHT BECAUSE THE BACKGROUND IS DARK, and inheriting was measured
 * wrong. Left at `color: inherit` the line picked up the page's body ink,
 * rgb(10,15,27), against the rgb(0,67,127) panel all three forms sit on — 1.92:1,
 * near-black on dark blue, which passes every "the links are present" check.
 * Walked up to the first ancestor with a real background colour on / , /contact-us
 * and /feedback/feedback-not-great at 1440, 768 and 390: rgb(0,67,127) on all
 * nine. White gives 9.96:1 on that panel.
 * ------------------------------------------------------------------------- */
#dm .gy-form-legal {
  clear: both;
  display: block;
  padding-top: 15px;
  font-size: 13px;
  line-height: 1.4;
  white-space: nowrap;
  color: #fff;
}
#dm .gy-form-legal a {
  color: #fff;
  text-decoration: underline;
}
#dm .gy-form-legal a:hover {
  text-decoration: none;
}
#dm .gy-form-legal span {
  opacity: 0.6;
}


/* ---------------------------------------------------------------------------
 * THE CARET MUST AGREE WITH THE PANEL.
 *
 * Duda flips the caret for the CURRENT section from the page's own class:
 *   … .unifiednav__item.dmNavItemSelected > .nav-item-text .icon::before
 *       { transform: rotate(180deg) }
 * which on live is honest, because live also leaves that section expanded. We
 * collapse every group on load (house standard), so on /camp-verde-az the
 * "Service Area" caret pointed UP over a closed panel — screenshotted at 390.
 *
 * Drive it from the same class the panel is driven by instead: down when closed,
 * up when open, whatever the current page is. Rotating .icon::before and NOT
 * .icon, because .icon already carries a per-band transform: scale() that a
 * transform on the element itself would silently overwrite.
 * ------------------------------------------------------------------------- */
#dm .layout-drawer .unifiednav__item_has-sub-nav > .nav-item-text .icon::before {
  transform: rotate(0deg) !important;
  transition: transform 0.3s ease-out;
}
#dm .layout-drawer .unifiednav__item-wrap_open > .unifiednav__item_has-sub-nav > .nav-item-text .icon::before {
  transform: rotate(180deg) !important;
}

/* ---------------------------------------------------------------------------
 * /blog-search's result count sits in the HERO, over a photograph.
 *
 * It inherits the page's body ink, rgb(0,0,0), while the H1 beside it is white —
 * and a contrast walk is no help here, because the first ancestor with a real
 * background-color is the white page body while what is actually behind the text
 * is a dark background-IMAGE. Screenshotted at 1440: black on a dark hero photo.
 * Take the heading's own ink.
 * ------------------------------------------------------------------------- */
/* Specificity, measured rather than guessed: the winning rule is
 * `#dm div.dmNewParagraph li, #dm div.dmNewParagraph p { color:#000 }` at
 * (1,1,2), so `#dm [data-mg-search-summary]` at (1,1,0) lost and the text stayed
 * black. Matching the same shape with the attribute added takes it to (1,2,2). */
#dm div.dmNewParagraph p[data-mg-search-summary] {
  color: #fff;
  font-size: 18px;
  margin-top: 6px;
}

/* ---------------------------------------------------------------------------
 * HOME PAGE — "Call to Schedule your Private Viewing!" wraps to an orphan.
 *
 * The label needs 357px on one line at 20px; the button was 280px, so it broke as
 * "Call to Schedule your Private / Viewing!" with one word alone on the second
 * line. The button was NOT constrained by its column — it had **112px of fixed
 * horizontal margin** (`margin: 20px 56.03px 10px 56.02px`, Duda's way of centring
 * an `align-center` button) inside a 392px usable column. Releasing the margins and
 * letting it fill the column gives 392px, and the label fits on ONE line.
 *
 * Measured usable column width vs the 357px the label needs:
 *     1440   392  ->  fits on one line once the 112px of margin goes
 *      768   189  ->  the CARD's own column is the limit here, not the button:
 *                     it already fills it, and no button width can hold 357px in
 *                     189px. Left as live has it; widening the card is a layout
 *                     change, not a button change.
 *      390   310  ->  already full-width, but 20px of its own side padding left
 *                     only 270px for a 321px label. Trimming to 10px gives 330px,
 *                     which fits on one line.
 *
 * `width: 100%` is a no-op at 768 and 390, where the button already fills its
 * column — the only band it moves is desktop.
 *
 * !important IS REQUIRED, and the first attempt without it changed nothing. The
 * page CSS carries `#dm .dmBody a.u_1111438320 { width: 280px !important }` (with
 * two earlier !important widths for the same selector before it), so no amount of
 * ordinary specificity wins. The 56px side margins are NOT declared — they are
 * `.align-center { margin: auto }` centring a 280px box in a 392px column, so they
 * collapse to 0 on their own once the width is released.
 * ------------------------------------------------------------------------- */
#dm .dmBody a.u_1111438320.u_1111438320 {
  width: 100% !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
}
.dmMobileBody #dm .dmBody a.u_1111438320.u_1111438320 {
  padding-left: 10px !important;
  padding-right: 10px !important;
}

/* Where the label still cannot fit one line — 390 has 290px for a 321px label
 * even with the padding trimmed, and 768's card column gives only 189px — balance
 * the lines instead of leaving "Viewing!" alone on the last one. text-wrap:balance
 * is ignored by browsers that do not support it, which leaves today's wrapping. */
#dm .dmBody a.u_1111438320.u_1111438320 .text {
  text-wrap: balance;
}
