/*
 * The only hand-written CSS in the port.
 *
 * Everything else is Duda's own cascade, lifted from the live document and
 * linked in its original order. These rules exist because a single static
 * document has to stand in for three device documents, or because a piece of
 * Duda's behaviour lived in its runtime rather than in its stylesheets.
 * Each one says which.
 */

/* 1. Both headers ship in one document; show the one for this width.
 *    The marker class is added BY THE PORTER onto Duda's own top-level chrome
 *    elements — never onto a wrapper around them, because the sheets are full
 *    of child and sibling selectors that an extra box would break, and never
 *    onto a hand-guessed selector, which missed the hamburger bar and painted
 *    it across every desktop page. */
/* Specificity matters here, not just !important. Duda styles these widgets
 * through selectors like `#dm .dmBody div.u_<id>` (1,2,1) carrying their own
 * !important, so a bare `.marker { display: none !important }` (0,1,0) loses
 * and the hidden variant keeps its box. Each override is written to outweigh
 * that shape. */
@media (max-width: 767px) {
  #dm .dmBody div.gy-chrome-d.gy-chrome-d,
  .gy-chrome-d.gy-chrome-d {
    display: none !important;
  }
}
@media (min-width: 768px) {
  #dm .dmBody div.gy-chrome-m.gy-chrome-m,
  .gy-chrome-m.gy-chrome-m {
    display: none !important;
  }
}

/* 1b. Photo galleries are laid out by Duda's runtime per device — 5 columns
 *     on desktop/tablet, 2 on a phone — so the phone tree has MORE ROWS, not
 *     just different attributes, and no attribute patch can express that.
 *     Both variants ship and the media query picks one. Desktop and tablet
 *     galleries are byte-identical on this site, so two variants suffice. */
@media (max-width: 767px) {
  #dm .dmBody div.gy-only-dt.gy-only-dt,
  .gy-only-dt.gy-only-dt {
    display: none !important;
  }
}
@media (min-width: 768px) {
  #dm .dmBody div.gy-only-m.gy-only-m,
  .gy-only-m.gy-only-m {
    display: none !important;
  }
}

/* 2. Parallax is frozen by Duda's RUNTIME on touch, not by its CSS. At 768 the
 *    live document computes background-attachment: scroll even though three
 *    !important rules declare it fixed. Without the runtime the ported rules
 *    win, the background attaches to the viewport, and a full-page capture
 *    renders the hero at roughly 3x zoom.
 *    Duda ships two spellings of the same idea, so both are matched, and the
 *    selector needs enough specificity to beat #dm .dmBody div.u_<id>. */
@media (max-width: 1024px) {
  #dm .dmBody div.dmSectionParallaxNew,
  #dm .dmBody div.dmSectionParallex,
  #dm .dmBody div[class*='dmSectionParallax'],
  #dm .dmBody div[class*='dmSectionParallex'] {
    background-attachment: scroll !important;
  }
}

/* 3. Honeypot. 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 as a difference. */
.gy-hp {
  position: absolute !important;
  left: -9999px !important;
  top: auto !important;
  width: 1px !important;
  height: 1px !important;
  overflow: hidden !important;
}

/* 4. reCAPTCHA is removed rather than reserved (its keys are Duda's and
 *    domain-restricted). Close the gap its container left behind so the form
 *    reads naturally. */
.dmform-recaptcha,
.dmforminput.recaptcha {
  display: none !important;
}

/* 5. The drawer's open state. Duda's rule keys off the `open` attribute; this
 *    only restores the transition and the overlay, which lived in the runtime. */
.layout-drawer-overlay {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.layout-drawer-overlay.is-open {
  opacity: 1;
  pointer-events: auto;
}

/* Duda outlines a field that fails validation. The rule lives in Duda's widget
 * sheet (WIDGET_CSS/4c5139e578083c6120faf67ede6189f0.css on live, verified
 * 2026-08-23) and this port dropped it, so the validation restored in
 * public/runtime.js would have marked fields with a class that styles nothing —
 * a rejected submit with no visible reason at all. Byte-for-byte live's rule. */
.dmform .inputError{outline:1px solid red!important}

/* 6. Scroll lock behind the open mobile drawer. runtime.js already toggles
 *    `dmDrawerOpen` on <body>, but nothing consumed it, so the page behind the
 *    panel scrolled freely — and on iOS the drawer scrolled the document
 *    instead of itself once its own content ran out.
 *    `position: fixed` is deliberately NOT used: it would reset scrollTop and
 *    jump the visitor back to the top when the drawer closes. */
html.dmDrawerOpen,
body.dmDrawerOpen {
  overflow: hidden !important;
  touch-action: none;
  overscroll-behavior: contain;
}
/* The panel itself must still scroll — it is taller than a phone viewport once
 * Areas Served is expanded. */
body.dmDrawerOpen .layout-drawer[open] {
  overflow-y: auto !important;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  overscroll-behavior: contain;
}

/* 7. The close affordance. Duda animates the hamburger's three slices into an
 *    X while the drawer is open; that transform lived in its runtime, not in
 *    the ported sheets, so the button stayed three bars. `hamburger-on-header`
 *    is position:fixed, so it stays visible however far the panel is scrolled. */
.layout-drawer-hamburger .hamburger__slice {
  transition: transform 0.25s ease, opacity 0.2s ease;
}
body.dmDrawerOpen .layout-drawer-hamburger .hamburger__slice:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
body.dmDrawerOpen .layout-drawer-hamburger .hamburger__slice:nth-child(2) {
  opacity: 0;
}
body.dmDrawerOpen .layout-drawer-hamburger .hamburger__slice:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}
body.dmDrawerOpen .layout-drawer-hamburger {
  z-index: 100000 !important;
}

/* 8. Privacy Policy / Terms links in the footer and under the contact form.
 *    Neither page exists on the live site, so there is no original styling to
 *    port — these match the footer's own copyright row (13px, white, the same
 *    left alignment) so they read as part of it rather than as an add-on. */
.gy-legal-links {
  font-size: 13px;
  line-height: 1.6;
  text-align: left;
  margin-top: 6px;
  color: #fff;
}
.gy-legal-links a {
  color: #fff !important;
  text-decoration: underline;
}
.gy-legal-links a:hover,
.gy-legal-links a:focus {
  text-decoration: none;
}
/* Under the form the row sits on the light canvas, not the green footer. */
.gy-legal-links--form {
  margin-top: 18px;
  color: inherit;
  text-align: center;
}
.gy-legal-links--form a {
  color: inherit !important;
}

/* 9. Drawer submenus. Duda's open-state rules for a VERTICAL nav with
 *    data-show-vertical-sub-items="HIDE" are present in the ported sheets and
 *    they do match the drawer's markup — but the collapsed default
 *    (`max-height: 0; opacity: 0`) still wins the cascade in the single merged
 *    document, so adding `unifiednav__item-wrap_open` alone left the submenu at
 *    zero height with the class correctly applied. This states the open state
 *    at a specificity nothing in the port outranks, scoped to the drawer so the
 *    desktop and tablet navs keep Duda's own hover behaviour untouched.
 *    Without it, Services and Areas Served are dead entries on a phone: they
 *    are href="#" with no page of their own, so the four service pages and the
 *    eighteen area pages have no route from the mobile menu at all. */
@media (max-width: 767px) {
  #dm .p_hfcontainer #mobile-hamburger-drawer nav.unifiednav[data-nav-structure="VERTICAL"]
    .unifiednav__item-wrap.unifiednav__item-wrap_open
    > .unifiednav__container_sub-nav[data-depth] {
    max-height: 200em !important;
    opacity: 1 !important;
    pointer-events: auto !important;
  }
  /* The chevron turns to point up while its group is open, as Duda's does. */
  #dm .p_hfcontainer #mobile-hamburger-drawer
    .unifiednav__item-wrap.unifiednav__item-wrap_open
    > a .icon-angle-down {
    transform: rotate(180deg);
  }
  #dm .p_hfcontainer #mobile-hamburger-drawer .unifiednav__item-wrap > a .icon-angle-down {
    transition: transform 0.25s ease;
  }
}

/* 10. Rules Duda scopes to [dmtemplateid="mobileHamburgerLayout"].
 *     That id lives on the page wrapper, and the merged document can only
 *     carry ONE — it carries the desktop layout's "StandardLayoutMultiD" —
 *     so twelve rules written for the phone layout never match. Two of them
 *     are visible: the hamburger stayed Duda's default grey (#787878) on a
 *     dark green bar, and it kept the base `top: 10px` instead of the 21.89px
 *     that centres a 40px button in an 83.77px bar, so it sat 12px above the
 *     logo and the phone icon.
 *     Restated here at the phone breakpoint, with the values Duda computed. */
@media (max-width: 767px) {
  .layout-drawer-hamburger.hamburger-on-header,
  body .layout-drawer-hamburger.hamburger-on-header {
    color: #fff !important;
    background-color: transparent !important;
  }
  .runtime-module-container .layout-drawer-hamburger,
  .layout-drawer-hamburger.hamburger-on-header {
    top: 21.89px !important;
  }

  /* The bar's three items — phone, logo, hamburger — on one centre line. The
   * column wrapper is already flex; it was just left at `align-items: normal`,
   * so the phone column stretched and its 39px icon box sat 14px low. */
  #mobile-hamburger-header .dmRespColsWrapper {
    align-items: center !important;
  }
  #mobile-hamburger-header .dmRespCol {
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
  }
  #mobile-hamburger-header .graphicWidget {
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
  }
  /* The phone glyph is an <svg> inside an inline <a>, so it sat on the text
   * baseline and hung ~14px below the middle of its own 39px box — which is
   * why it read as low against the logo even once the column was centred.
   * Taking it out of inline flow is what actually lines it up. */
  #mobile-hamburger-header .graphicWidget > a {
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    height: 100% !important;
    width: 100% !important;
  }
  #mobile-hamburger-header .graphicWidget svg {
    display: block !important;
  }
}

/* 11. Footer legal links, centred. */
.gy-legal-links {
  text-align: center;
}

/* 12. Accordions. Duda's widget is server-rendered markup plus a runtime that
 *     was not ported, so every FAQ on 56 pages was inert: the panels sit at
 *     `max-height: 0` and nothing ever changed it. The open/closed state is
 *     carried by styled-components class names hashed per height
 *     (`dygwmn` = 0, `cCuScM` = 92px …), which is not something to drive from
 *     script — so the state moves to `gy-acc-open` on the item and an inline
 *     max-height, and these rules outrank the hashed pair.
 *     `gy-` prefixed so withMarkers() keeps it through a class patch. */
[data-grab="accordion-item-container"] .gy-acc-panel {
  overflow: hidden;
  transition: max-height 0.32s ease, opacity 0.24s ease;
  will-change: max-height;
}
/* No closed-state rule here on purpose: the panel's max-height is driven
 * inline by runtime.js, and an `!important` here would win over it and freeze
 * the animation half-open. Before the script runs, Duda's own hashed classes
 * already hold the right open/closed heights. */
[data-grab="accordion-item-title-wrapper"] {
  cursor: pointer;
  user-select: none;
}
[data-grab="accordion-item-arrow"] svg {
  transition: transform 0.3s ease !important;
  transform: rotate(0deg) !important;
}
[data-grab="accordion-item-container"].gy-acc-open [data-grab="accordion-item-arrow"] svg {
  transform: rotate(180deg) !important;
}

/* 13. The review carousel on /testimonials. Thirteen slides ship in the
 *     markup, all but one at `opacity: 0` — FlexSlider's fade mode leaves them
 *     stacked and its script does the crossfade. Without it the page showed
 *     one review and the arrows did nothing.
 *     `fadeInUp` is the per-slide animation the markup asks for, so the
 *     incoming slide rises as it fades. */
/* Show exactly one slide rather than stacking all fourteen. Crossfading them
 * in place did not survive contact with the markup: the slides are floated
 * with margin-right:-100%, so which one paints depends on float order and the
 * z-index the capture froze into each element. display:none leaves no
 * ambiguity — one slide is in flow, and it is the right one.
 *
 * Specificity, not just `!important`. Duda ships
 *   .flexslider.ed-version:not([layout]) .slides > li[layout="center"]
 *     { display: flex !important }
 * which is (0,5,1) and forces EVERY slide into flow. A plain
 * `.flexslider .slides > li.gy-slide` is (0,3,1) and loses even with
 * `!important`. The class is repeated to outweigh it — the same trick, and for
 * the same reason, as the `.gy-chrome-d.gy-chrome-d` markers in rule 1.
 *
 * The active slide is `flex`, not `block`: that is the display Duda's own rule
 * sets, and it is what centres the review inside the frame.
 *
 * There is deliberately NO fade declared here. The review text is
 * `visibility: hidden` until `.slide-inner` carries `.animated`, and each
 * slide names its own entrance in an `animation` attribute — `fadeInUp`,
 * which is exactly the rise-from-below the page is meant to have. runtime.js
 * applies Duda's own classes, so the motion is Duda's, not an imitation. */
.flexslider .slides > li.gy-slide.gy-slide.gy-slide:not(.gy-slide-active) {
  display: none !important;
}
.flexslider .slides > li.gy-slide.gy-slide.gy-slide.gy-slide-active {
  display: flex !important;
}

/* Duda ships the arrows as text links that its stylesheet turns into glyphs;
 * keep them clickable and above the slides. */
.flexslider .flex-direction-nav a {
  cursor: pointer;
  z-index: 5;
}
@media (prefers-reduced-motion: reduce) {
  .flexslider .slides > li .slide-inner.animated { animation: none !important; }
  [data-grab="accordion-item-container"] .gy-acc-panel { transition: none; }
}

/* 14. The gallery lightbox. PhotoSwipe's markup and stylesheet are both
 *     ported — only its script is missing, so clicking a photo did nothing.
 *     runtime.js drives the existing dialog; these rules cover the parts
 *     PhotoSwipe's own JS would otherwise set inline, and hide the three
 *     controls that are not reimplemented (share, fullscreen, zoom) rather
 *     than leaving dead buttons on screen. */
.pswp.gy-pswp-open {
  display: block !important;
  position: fixed !important;
  opacity: 1;
}
.gy-pswp-open .pswp__bg { opacity: 1; }
.gy-pswp-open .pswp__scroll-wrap { position: absolute; inset: 0; }
.gy-pswp-open .pswp__item {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.gy-pswp-open .pswp__item img {
  max-width: 92vw;
  max-height: 86vh;
  width: auto;
  height: auto;
  object-fit: contain;
  box-shadow: 0 8px 40px rgba(0, 0, 0, 0.5);
  transition: opacity 0.25s ease;
}
.gy-pswp-open .pswp__button--share,
.gy-pswp-open .pswp__button--fs,
.gy-pswp-open .pswp__button--zoom { display: none !important; }
.gy-pswp-open .pswp__ui { opacity: 1; }
.gy-pswp-open .pswp__caption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 12px 16px;
  color: #ccc;
  background: rgba(0, 0, 0, 0.5);
  font-size: 14px;
  text-align: center;
}
/* The thumbnails are anchors with no href; say so to the pointer. */
.photoGalleryThumbs .image-container a { cursor: zoom-in; }

/* 15. The legal pages' content row. They are ported documents now (see
 *     tools/build-legal-pages.py), so they inherit the site's header, hero
 *     band, footer, map and type. What they do NOT inherit is a width cap:
 *     the article rows on a real page sit inside a wrapper carrying one, and
 *     a bare .dmRespRow under #dm_content would run the full window on a wide
 *     screen. 1200px matches the `rows-1200` the layout is built on.
 *     Duda's reset strips list markers, so the bulleted clauses need theirs
 *     back — they are lists in the source templates and read as lists. */
.gy-legal-row {
  padding-block: 44px 64px;
}
.gy-legal-row > .dmRespColsWrapper {
  max-width: 1200px;
  margin-inline: auto;
}
.gy-legal-row h1 {
  margin-bottom: 6px;
}
.gy-legal-row h2 {
  margin-top: 34px;
  margin-bottom: 8px;
}
.gy-legal-row ul.rteBlock {
  list-style: disc outside;
  margin: 0 0 16px 24px;
  padding-left: 4px;
}
.gy-legal-row ul.rteBlock li {
  margin-bottom: 6px;
}
