/* 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: /blog serves 10 of 37 posts and fetches the rest from Duda's backend,
 * which we do not have. All 37 ship; runtime.js reveals them 10 at a time. */
#dm .mg-blog-hidden.mg-blog-hidden.mg-blog-hidden {
  display: none !important;
}

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

/* ---------------------------------------------------------------------------
 * LAST-CARD PADDING — the cost of shipping the hidden pages.
 *
 * Duda's own rule is
 *
 *     .postArticle:not(:last-child) { padding-bottom: 30px }
 *
 * On live that is exact: /blog holds ten cards and the tenth IS :last-child, so
 * it takes no bottom padding. Our build ships ALL 37 so the "Show More" control
 * has something to reveal, and `display:none` DOES NOT REMOVE AN ELEMENT FROM THE
 * SIBLING TREE — so the tenth card has 27 hidden siblings after it, stops being
 * :last-child, and gains 30px live does not have.
 *
 * Measured at 375: every one of the first nine cards matched live to the pixel
 * (298/328/298/328/328/358/298/358/298) and the tenth was 280 against live's 250.
 * That 30px pushed "Show More" and the whole footer down by exactly 30px at all
 * three widths — /blog scored 4.586% / 2.713% / 2.410% with a constant +30px
 * height delta, the signature of one shared element rather than a per-page fault.
 *
 * build-pages.py already stamps .lastArticle on the last VISIBLE card, exactly as
 * Duda does, so keying on that restores live's geometry without touching any
 * other card. runtime.js re-stamps it as each batch is revealed.
 * ------------------------------------------------------------------------- */
#dm .postArticle.lastArticle.lastArticle {
  padding-bottom: 0 !important;
}

/* ---------------------------------------------------------------------------
 * TABLET DRAWER WIDTH — measured override, and the only new layout CSS this
 * migration writes.
 *
 * The ported cascade's generic `.layout-drawer` rule gives 75vw, which is CORRECT
 * for the mobile drawer and wrong for the tablet one. Measured on live at two
 * widths, because one data point cannot tell a viewport fraction from a fixed
 * canvas fraction:
 *
 *              viewport 768        viewport 1000       implies
 *   LIVE       269px               350px               0.350 -> 35vw
 *   BUILD      576px               750px               0.750 -> 75vw (ported)
 *
 * A constant px at both widths would have meant a fixed width; a constant RATIO
 * means viewport-relative, so 35vw is derived rather than guessed.
 *
 * MOBILE NEEDS NO OVERRIDE: live and the build both measure 281px at 375 (75vw),
 * so the ported rule is already right there and is left alone.
 *
 * BACKGROUND COLOUR NEEDS NO OVERRIDE EITHER: both drawers are rgb(255,255,255)
 * on live and in the build. This file inherited a `background-color: rgb(39,94,176)`
 * override from the PREVIOUS client, along with 40vw/80vw widths and a 190px logo
 * cap — none of it true here. It was deleted rather than carried, and this rule is
 * what the measurement actually supports.
 *
 * INVISIBLE TO THE PIXEL GATE, correctly: the drawer is closed in every capture
 * and parked off-screen (left = -width on both sides), so every page scores the
 * same either way. Found by opening it with a real click in check-interactive.
 * ------------------------------------------------------------------------- */
@media (min-width: 768px) and (max-width: 1024px) {
  #dm #hamburger-drawer.layout-drawer,
  #dm #hamburger-drawer.layout-drawer[data-origin="side"] {
    width: 35vw !important;
  }
}

/* ---------------------------------------------------------------- blog search
 * The empty state for the in-page blog search. Duda has no equivalent — its
 * search left the page for a server-rendered /blog-search — so this is the one
 * piece of that feature with no rule to port. Kept to the listing's own type
 * scale rather than introducing a new one. */
.mg-blog-no-results {
  padding: 30px 10px;
  text-align: center;
  font-size: 16px;
  color: #666;
}


/* ------------------------------------------------- promoted-heading size pin
 * Where a page had no title banner to promote, seo.mjs raises its first h2 to
 * h1 and tags it `gy-h1-pinned`. That promotion is NOT visually free the way the
 * banner's is: the banner carries `size-45`/`m-size-28` and renders identically
 * under either tag, while a bare heading falls through to whatever `h1`
 * computes to. Measured on /free-rent-analysis, the same element reads
 *   1440: h2 30px -> h1 45px    768: h2 30px -> h1 45px    375: h2 27px -> h1 30px
 * so these three rules hold it at the h2 values it shipped with, per band. */
#dm .dmBody h1.gy-h1-pinned { font-size: 30px; }
@media (max-width: 767px) {
  #dm .dmBody h1.gy-h1-pinned { font-size: 27px; }
}

/* --------------------------------------------------- demoted-heading size pin
 * /realtors ships two h1s on live. The second is demoted to h2 so the page has
 * exactly one, but a demotion is as visible as a promotion and in the more
 * noticeable direction: measured on live that heading is 45px at 1440 and 768
 * and 30px at 375, while a plain h2 renders it at 30px on desktop. These rules
 * hold it at live's sizes, so the tag changes and the page does not. */
#dm .dmBody h2.gy-h1-demoted { font-size: 45px; }
@media (max-width: 767px) {
  #dm .dmBody h2.gy-h1-demoted { font-size: 30px; }
}

/* ------------------------------------------------------- body-copy interlink
 * The business-name link back to the homepage that seo.mjs inserts once per
 * page. Underline + bold marks it as an interlink; the rule is scoped to
 * .gy-interlink and never to a tag, so nothing in the nav, the drawer, the
 * footer or a heading can pick the treatment up — the checklist puts interlink
 * styling on body text only. Colour is inherited so the link sits in whatever
 * paragraph it lands in rather than importing a new one. */
#dm .gy-interlink {
  color: inherit;
  font-weight: 700;
  text-decoration: underline;
}

/* --------------------------------------------------- legal line under a form
 * Sits after the submit control on all 11 forms. Deliberately quieter than the
 * form's own type so it reads as a footnote and not as another field label; the
 * links carry the underline that marks them as links and nothing else. */
.gy-form-legal {
  clear: both;          /* Duda FLOATS .dmformsubmit — without this the note is
                           taken beside the button rather than under it, and
                           measures 6px ABOVE the submit it is meant to follow.
                           The form's own .dmWidgetClear span is the same fix. */
  margin: 14px 0 4px;
  font-size: 12px;
  line-height: 1.5;
  opacity: 0.85;   /* colour itself is set per form from its own label colour —
                      see formLegal() in runtime.js. Four of the 11 forms sit on
                      the brand blue and label in white; a declared colour here
                      would be unreadable on one group or the other. */
}
.gy-form-legal a {
  /* `inherit` matters more than it looks: the ported cascade colours every link
     inside .dmInner rgb(70,57,57), a dark brown that is unreadable on the brand
     blue four of the 11 forms sit on. The note's own colour is set per form from
     that form's label colour, so inheriting it puts the links on the same
     footing as the sentence around them. !important because the cascade rule is
     more specific than a plain class selector here. */
  color: inherit !important;
  text-decoration: underline;
  font-weight: 600;
}

/* --------------------------------------------------------------- popup pages
 * Duda shows /free-rent-analysis and /free-assessment as MODALS, not pages —
 * the links carry link_type="popup". The port kept #dmPopup, #dmPopupMask and
 * their CSS but not the JS that sizes and reveals them, so the captured rules
 * are the ones Duda leaves in place BEFORE its runtime takes over:
 *     #dmPopup      { opacity: 0 }        <- revealed by JS
 *     .dmPopupMask  { width: 10px }       <- stretched by JS
 * These finish that job in CSS instead, at live's measured geometry (homepage,
 * 1440x900, real mouse): popup 750x800 at (335, 50), mask rgba(0,0,0,.5) at
 * z-index 1000000999. */
#dmPopup {
  opacity: 1;
  /* The captured rule also leaves the popup at `pointer-events: none` — Duda's
     runtime flips it to auto on open. Without this the modal renders perfectly
     and nothing in it can be clicked: the close button, the form fields and the
     submit are all inert, and the mask underneath swallows every click, so it
     reads as a modal that cannot be dismissed. */
  pointer-events: auto;
  top: 50px;
  left: 50%;
  transform: translateX(-50%);
  width: min(750px, 94vw);
  max-width: 750px;
  height: auto;
  max-height: calc(100vh - 100px);
  overflow-y: auto;
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35);
  z-index: 1000009999;
}
.dmPopupMask {
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
}
/* The injected page carries its own full-bleed section padding, which inside a
 * 750px modal reads as a large empty margin. Live's modal is the content only. */
#dmPopup .data > div,
#dmPopup .dmRespRowsWrapper {
  max-width: 100%;
}
#dmPopup .dmRespRow {
  padding-left: 0;
  padding-right: 0;
}
.dmPopupClose {
  cursor: pointer;
}

/* ------------------------------------------------------- lightbox background
 * PhotoSwipe's own .pswp__bg ships at 0.85 alpha over #000 in the captured CSS.
 * Darkened so the photograph separates from the page behind it rather than
 * competing with it — the gallery pages are bright, and at 0.85 the underlying
 * layout stayed legible enough to read as clutter around the image. */
#dm ~ .pswp .pswp__bg,
.pswp .pswp__bg {
  background: rgba(0, 0, 0, 0.92) !important;
  opacity: 1 !important;
}
