.elementor-button-icon svg { width: 17px; height: 17px; }
.elementor-button-icon { transition: transform .25s cubic-bezier(.22,.61,.36,1); }
.elementor-button:hover .elementor-button-icon { transform: translateX(4px); }
.elementor-button { white-space: nowrap; }
.elementor-button-text {white-space: normal;}
.s-hazard {
  background: repeating-linear-gradient(-45deg, #E8BE4E 0 10px, #14181F 10px 20px);
  animation: hazardSlide 1.1s linear infinite;
}
@keyframes hazardSlide { from { background-position: 0 0; } to { background-position: 28.284px 0; } }

@keyframes bgDrift { from { transform: scale(1); } to { transform: scale(1.085); } }
.s-hero-bg img, .s-pledge-photo img, .s-trust-photo img, .s-cta-photo img {
  animation: bgDrift 26s ease-in-out infinite alternate; will-change: transform; backface-visibility: hidden;
}
.s-pledge-photo img { animation-duration: 34s; }
.s-trust-photo img  { animation-duration: 30s; }
.s-cta-photo img    { animation-duration: 22s; }

.s-hero-h1 .zap { position: relative; display: inline-block; font-style: normal; color: #E8BE4E; animation: zapGlow 4.6s ease-in-out infinite; }
.s-hero-h1 .zap::after {
  content: ""; display: inline-block; width: .5216em; height: .72em; margin-left: .1em;
  vertical-align: baseline;
  background: url("/wp-content/uploads/2026/08/icon-bolt-gold.svg") no-repeat center / contain;
  animation: zapFlicker 3.6s steps(1, end) infinite;
}
@keyframes zapGlow { 0%,100% { text-shadow: 0 0 0 rgba(232,190,78,0); } 50% { text-shadow: 0 0 30px rgba(232,190,78,.5); } }
@keyframes zapFlicker { 0%,84%,100% { opacity: 1; } 86% { opacity: .2; } 88% { opacity: 1; } 91% { opacity: .45; } 93% { opacity: 1; } }

.s-scrollcue-line { position: relative; }
.s-scrollcue-line::after {
  content: ""; position: absolute; left: 0; top: 0; width: 1px; height: 16px; background: #E8BE4E;
  animation: cue 1.9s cubic-bezier(.22,.61,.36,1) infinite;
}
@keyframes cue { 0% { transform: translateY(-16px); } 100% { transform: translateY(46px); } }

.s-hdr:not(.elementor-sticky--effects) {
  background: linear-gradient(180deg, rgba(4,10,32,.5) 0%, rgba(4,10,32,.08) 82%, rgba(4,10,32,0) 100%);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
}
.s-hdr { transition: background .3s cubic-bezier(.22,.61,.36,1), box-shadow .3s cubic-bezier(.22,.61,.36,1); }
.s-hdr-logo img { filter: drop-shadow(0 2px 12px rgba(4,10,32,.55)); }
.s-hdr.elementor-sticky--effects,
body:has(.e-off-canvas[aria-hidden="false"]) .s-hdr {
  background: #fff; box-shadow: 0 8px 30px -16px rgba(4,10,32,.24);
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
.s-hdr.elementor-sticky--effects .s-hdr-logo img,
body:has(.e-off-canvas[aria-hidden="false"]) .s-hdr-logo img { filter: none; }
.s-hdr.elementor-sticky--effects .s-hdr-nav .elementor-item { color: #282828 !important; }
.s-hdr.elementor-sticky--effects .s-hdr-nav .elementor-item:hover { color: #0027E5 !important; }
.s-hdr.elementor-sticky--effects .s-hdr-nav .sub-arrow svg { fill: #282828 !important; }
.s-hdr.elementor-sticky--effects .s-hdrcall { background: #E8BE4E; border-color: #E8BE4E; }
.s-hdr.elementor-sticky--effects .s-hdrcall:hover { background: #D3A73A; border-color: #D3A73A; }
.s-hdr.elementor-sticky--effects .s-hdrcall-ico { background: #040A20; }
.s-hdr.elementor-sticky--effects .s-hdrcall-ico svg { fill: #E8BE4E; }
.s-hdr.elementor-sticky--effects .s-hdrcall-b .elementor-heading-title { color: rgba(15,18,22,.62); }
.s-hdr.elementor-sticky--effects .s-hdrcall-strong .elementor-heading-title { color: #0F1216; }
.s-hdr.elementor-sticky--effects .s-burger,
body:has(.e-off-canvas[aria-hidden="false"]) .s-burger { background: #F7F7F8 !important; border-color: #E6E4E7 !important; }
.s-hdr.elementor-sticky--effects .s-burger-bar,
body:has(.e-off-canvas[aria-hidden="false"]) .s-burger-bar { background: #0F1216 !important; }

.s-hdr-nav .elementor-nav-menu--main .sub-arrow { margin-left: 6px; padding: 0; display: inline-flex; align-items: center; }
.s-hdr-nav .elementor-nav-menu--main .sub-arrow svg { width: 9px; height: 9px; fill: currentColor; }
.s-hdr-nav .elementor-nav-menu--main .sub-arrow i { font-size: 9px; line-height: 1; }
.s-hdr-nav .elementor-nav-menu--main li:hover .sub-arrow svg,
.s-hdr-nav .elementor-nav-menu--main li:focus-within .sub-arrow svg { transform: rotate(180deg); }
.s-hdr-nav .elementor-nav-menu--main .sub-arrow svg { transition: transform .22s cubic-bezier(.22,.61,.36,1); }

.s-hdr-nav .elementor-nav-menu--main .elementor-item:after { left: 13px; right: 13px; bottom: 2px; width: auto; }

.s-burger-bar { transition: transform .24s cubic-bezier(.22,.61,.36,1), opacity .18s cubic-bezier(.22,.61,.36,1); }
body:has(.e-off-canvas[aria-hidden="false"]) .s-burger-bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
body:has(.e-off-canvas[aria-hidden="false"]) .s-burger-bar:nth-child(2) { opacity: 0; }
body:has(.e-off-canvas[aria-hidden="false"]) .s-burger-bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

@media (max-width: 1460px) {
  .s-hdr-nav .elementor-nav-menu--main .elementor-item { padding-left: 10px !important; padding-right: 10px !important; font-size: 12px !important; }
  .s-hdrcall-strong .elementor-heading-title { font-size: 15px !important; }
}

@media (max-width: 1240px) { .s-infobar-hide1240 { display: none; } }

@media (max-width: 820px) {
  .s-infobar > .e-con-inner { justify-content: center !important; column-gap: 20px !important; row-gap: 8px !important; text-align: center; padding-block: 9px !important; }
  .s-infobar-right { flex-grow: 0 !important; column-gap: 20px !important; }
  .s-infobar-item .elementor-heading-title { font-size: 12.5px !important; }
}

@media (max-width: 520px) { .s-infobar-item { display: none !important; } }

.elementor-widget-text-editor p:last-child { margin-bottom: 0; }

.s-bgfill, .s-bgfill > .e-con-inner,
.s-bgfill .elementor-widget-image, .s-bgfill .elementor-widget-image > * { height: 100%; }

.elementor-widget-icon { line-height: 0; }

body { -webkit-font-smoothing: antialiased; }

@keyframes strebMarquee { to { transform: translateX(-50%); } }
.s-marquee-track { animation: strebMarquee 34s linear infinite; }
@media (prefers-reduced-motion: reduce) { .s-marquee-track { animation: none; } }

.s-ftr-links .elementor-icon-list-items { display: block !important; }
.s-ftr-links .elementor-icon-list-item { display: block !important; line-height: 27px !important; }
.s-ftr-links .elementor-icon-list-item > a { display: inline !important; font-size: 14.5px !important; line-height: 1.4 !important; }
.s-ftr-links .elementor-icon-list-item > a { transition: color .18s cubic-bezier(.22,.61,.36,1), padding-left .18s cubic-bezier(.22,.61,.36,1); }
.s-ftr-links .elementor-icon-list-item > a:hover { color: var(--e-global-color-primary); padding-left: 4px; }
.s-ftr-copy a { color: var(--e-global-color-text); border-bottom: 1px solid var(--e-global-color-line); text-decoration: none; }
.s-ftr-copy a:hover { color: var(--e-global-color-primary); border-bottom-color: var(--e-global-color-primary); }
.s-ftr-tel:hover .elementor-heading-title { color: var(--e-global-color-primary); }

@media (max-width: 640px) { body { padding-bottom: 58px; } }

@media (min-width: 1021px) {
  .s-ftr-grid .s-ftr-brand { flex: 1.5 1 0 !important; width: auto !important; min-width: auto !important; }
  .s-ftr-grid .s-ftr-col   { flex: 1 1 0 !important;   width: auto !important; min-width: auto !important; }
}

@media (max-width: 600px) {
  .s-ftr-grid .s-ftr-brand,
  .s-ftr-grid .s-ftr-col { width: 100% !important; flex: 0 0 100% !important; }
}

.s-drawer .e-off-canvas__main {
  top: var(--streb-drawer-top, 78px) !important;
  height: calc(100% - var(--streb-drawer-top, 78px)) !important;
  overflow-y: auto;
  background: #fff;
}
.s-drawer .e-off-canvas__overlay { background: transparent; }
body.e-off-canvas__no-scroll-animation:not(:has(.e-off-canvas[aria-hidden="false"])) { overflow: visible !important; }
.s-drawer .e-off-canvas__main { align-items: stretch !important; }
.s-drawer .e-off-canvas__content {
  padding: 24px clamp(20px,5vw,60px) 40px;
  height: 100% !important; max-height: 100%; overflow-y: auto; overscroll-behavior: contain;
}

.s-hdr-nav .elementor-nav-menu > li { position: relative; }
.s-hdr-nav .elementor-nav-menu .sub-menu {
  display: grid !important; position: absolute !important; top: 100% !important;

  left: 0 !important; right: auto !important; margin-left: 50%;
  transform: translateX(-50%) translateY(6px);
  min-width: 268px; width: max-content; background: #fff; padding: 8px; gap: 1px;
  box-shadow: 0 26px 60px -22px rgba(4,10,32,.4);
  opacity: 0; pointer-events: none; border: 0;
  transition: opacity .2s cubic-bezier(.22,.61,.36,1), transform .2s cubic-bezier(.22,.61,.36,1);
}
.s-hdr-nav .elementor-nav-menu > li:hover > .sub-menu,
.s-hdr-nav .elementor-nav-menu > li:focus-within > .sub-menu {
  opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0);
}
.s-hdr-nav .sub-menu li { margin: 0; }
.s-hdr-nav .sub-menu a.elementor-sub-item {
  display: block; padding: 12px 16px;
  font-family: "Roboto", system-ui, sans-serif !important; font-weight: 700 !important;
  font-size: 12.5px !important; line-height: 1.5 !important;
  letter-spacing: .04em !important; text-transform: uppercase !important;
  color: var(--e-global-color-text); white-space: nowrap; background: none; border: 0;
  transition: background .16s cubic-bezier(.22,.61,.36,1), color .16s cubic-bezier(.22,.61,.36,1);
}
.s-hdr-nav .sub-menu a.elementor-sub-item:hover,
.s-hdr-nav .sub-menu a.elementor-sub-item:focus {
  background: var(--e-global-color-paper2); color: var(--e-global-color-primary);
}
.s-hdr-nav .elementor-nav-menu > li > a .sub-arrow svg { transition: transform .22s cubic-bezier(.22,.61,.36,1); }
.s-hdr-nav .elementor-nav-menu > li:hover > a .sub-arrow svg { transform: rotate(180deg); }

 .s-hero { position: relative; }
 .s-hero::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(4,10,32,.70) 0%, rgba(4,10,32,.26) 16%, rgba(4,10,32,0) 30%),
    linear-gradient(0deg,   rgba(4,10,32,.46) 0%, rgba(4,10,32,.10) 20%, rgba(4,10,32,0) 34%),
    linear-gradient(96deg,  rgba(4,10,32,.90) 0%, rgba(4,10,32,.84) 24%, rgba(4,10,32,.62) 40%, rgba(4,10,32,.30) 52%, rgba(4,10,32,.10) 64%, rgba(4,10,32,.02) 78%, rgba(4,10,32,0) 100%);
}

 .s-hero-content { position: relative; z-index: 2; }

@media (max-width: 900px) {
   .s-hero::after {
    background:
      linear-gradient(180deg, rgba(4,10,32,.74) 0%, rgba(4,10,32,.24) 14%, rgba(4,10,32,0) 28%),
      linear-gradient(0deg,   rgba(4,10,32,.96) 0%, rgba(4,10,32,.93) 44%, rgba(4,10,32,.78) 62%, rgba(4,10,32,.44) 78%, rgba(4,10,32,.12) 92%, rgba(4,10,32,0) 100%);
  }
}

@media (max-width: 560px) {
   .s-hero-act .elementor-widget-button { width: 100% !important; }
   .s-hero-act .elementor-button { width: 100% !important; justify-content: center; }
}

@media (max-width: 900px) { .s-hero-bg img { object-position: 59% 42% !important; }  }

 .s-creds-row .elementor-widget-image img {
  mix-blend-mode: multiply;
  transition: opacity .28s cubic-bezier(.22,.61,.36,1), transform .28s cubic-bezier(.22,.61,.36,1);
}
 .s-creds-row .elementor-widget-image:hover img { opacity: 1; transform: translateY(-2px); }

@media (max-width: 760px) {
   .s-creds-row { row-gap: 8px !important; }
   .s-creds-row .elementor-widget-image { margin: 0 10px !important; }
   .s-creds-row .elementor-widget-image img { height: 34px !important; }
   .s-creds-label .s-creds-rule { width: 20px !important; }
}

 .s-about-media { position: relative; }
 .s-about-media > .s-about-photo { position: absolute; inset: 0; }
 .s-about-media > .s-about-photo .elementor-widget-container,
 .s-about-media > .s-about-photo figure { height: 100%; }
 .s-about-media > .s-about-photo img { object-position: 42% 28% !important; }
@media (max-width: 1020px) {
   .s-about-media > .s-about-photo img { object-position: 57% 28% !important; }
}

 .s-about-media::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 44%; z-index: 1;
  background: linear-gradient(0deg, rgba(15,18,22,.86) 0%, rgba(15,18,22,.5) 42%, rgba(15,18,22,0) 100%);
  pointer-events: none;
}

 .s-about-cap { z-index: 3; max-width: 60%; }

 .s-tick-chip .elementor-icon { line-height: 0; display: block; }
 .s-tick-chip .elementor-icon svg { width: 25px; height: 25px; display: block; }

 .s-metric-counter .elementor-counter-number-wrapper { align-items: baseline; justify-content: flex-start; }
 .s-metric-counter .elementor-counter-number { font-variant-numeric: tabular-nums; }

 .s-metric-counter .elementor-counter-number-wrapper::after {
  content: "200b"; display: inline-block; width: 0; overflow: hidden;
  font: 400 18px/1.5 "Open Sans", sans-serif;
}
 .s-metric-counter .elementor-counter-number-suffix {
  font-family: "Roboto", sans-serif; font-weight: 900;
  font-size: clamp(22px, 2.4vw, 34px); line-height: 1.5;
  letter-spacing: -.03em; color: #BA9845;
}
 .s-metric-counter .elementor-counter-title { justify-content: flex-start; text-align: left; }

@media (max-width: 860px) {
   .s-metrics { row-gap: 26px !important; }
   .s-metrics > .s-metric { width: 50% !important; }
   .s-metrics > .s-metric:nth-child(odd) { border-left: 0 !important; padding-left: 0 !important; }
}
@media (max-width: 600px) {
   .s-ticks > .s-tick { width: 100% !important; }
   .s-about-cap { max-width: 52% !important; }
}
@media (max-width: 480px) {
   .s-metrics > .s-metric { width: 100% !important; border-left: 0 !important; padding-left: 0 !important; }
   .s-metrics > .s-metric + .s-metric { border-top: 1px solid #E6E4E7 !important; padding-top: 22px !important; }
}

 .s-rail { position: relative; }
 .s-rail-track {
  overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain;
  scroll-snap-type: x proximity; scroll-behavior: smooth;
  scrollbar-width: none; -ms-overflow-style: none;
}
 .s-rail-track::-webkit-scrollbar { display: none; }
 .s-scard { scroll-snap-align: start; }
@media (max-width: 640px) { .s-rail-track { scroll-snap-type: x mandatory; } }

 .s-scard-desc .elementor-widget-container,
 .s-scard-desc p {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

 .s-fillphoto { position: absolute; inset: 0; z-index: 0; }
 .s-fillphoto .elementor-widget-container,  .s-fillphoto figure { height: 100%; }

 .s-scard-panel {
  background: linear-gradient(0deg,
    rgba(4,10,32,.97) 0%, rgba(4,10,32,.93) 46%,
    rgba(4,10,32,.72) 74%, rgba(4,10,32,.30) 90%, rgba(4,10,32,0) 100%);
}

 .s-scard .s-fillphoto img { transition: transform .8s cubic-bezier(.16,1,.3,1); }
 .s-scard:hover .s-fillphoto img { transform: scale(1.06); }
 .s-scard::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px; z-index: 4;
  background: #E8BE4E; transform: scaleX(0); transform-origin: left;
  transition: transform .4s cubic-bezier(.16,1,.3,1);
}
 .s-scard:hover::after { transform: scaleX(1); }
 .s-scard-go-icon .elementor-icon { line-height: 0; display: block; transition: transform .3s cubic-bezier(.22,.61,.36,1); }
 .s-scard:hover .s-scard-go-icon .elementor-icon { transform: translateX(5px); }

 .s-rail-nav .elementor-icon {
  box-shadow: 0 10px 30px -10px rgba(15,18,22,.45);
  transition: background .22s cubic-bezier(.22,.61,.36,1), color .22s cubic-bezier(.22,.61,.36,1);
  line-height: 0;
}
 .s-rail-nav:hover .elementor-icon { background-color: #0027E5; color: #fff; }
 .s-rail-nav:hover .elementor-icon svg { fill: #fff; }
 .s-rail-nav[hidden],  .s-rail-nav.is-disabled { opacity: 0; pointer-events: none; }
@media (max-width: 640px) { .s-rail-nav { display: none !important; } }

@media (max-width: 860px) {
   .s-shead { flex-direction: column !important; align-items: flex-start !important; gap: 18px !important; }
   .s-shead > .s-shead-main { width: 100% !important; }
   .s-shead > .s-shead-lead { width: 100% !important; align-self: flex-start !important; text-align: left !important; }
}

 .s-pledge { position: relative; isolation: isolate; }
 .s-pledge-photo { overflow: hidden; }
 .s-pledge .s-pledge-photo img { object-position: 64% 56% !important; }
 .s-pledge::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(100deg, rgba(0,14,51,.95) 0%, rgba(0,27,139,.86) 52%, rgba(0,39,229,.62) 100%);
}
 .s-pledge::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: url("/wp-content/uploads/2026/08/cta-bg-blue-1.jpg") center/cover no-repeat;
  opacity: .22; mix-blend-mode: overlay;
}
 .s-pledge-mark-sw12,  .s-pledge-mark-sw10 { display: none; }
@media (max-width: 900px) {
   .s-pledge-mark-sw17 { display: none; }
   .s-pledge-mark-sw12 { display: block; }
}
@media (max-width: 560px) {
   .s-pledge-mark-sw12 { display: none; }
   .s-pledge-mark-sw10 { display: block; }
   .s-pledge-foot { width: fit-content !important; min-width: 100%; }
}
@media (max-width: 900px) { .s-pledge-foot { width: fit-content !important; min-width: 100%; } }

@media (max-width: 640px) {
   .s-pledge-foot { width: 100% !important; min-width: 0 !important; }
   .s-pledge-foot .elementor-button { white-space: normal !important; }
}

 .s-trust { position: relative; isolation: isolate; }
 .s-trust-photo { overflow: hidden; }
 .s-trust .s-trust-photo img { object-position: 50% 46% !important; }

 .s-trust::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background:
    linear-gradient(100deg,rgba(4,10,32,.97) 0%,rgba(4,10,32,.94) 40%,rgba(4,10,32,.90) 68%,rgba(4,10,32,.87) 100%),
    linear-gradient(0deg,rgba(4,10,32,.5) 0%,rgba(4,10,32,0) 55%);
}

 .s-promise-ico .elementor-icon { border-style: solid; border-width: 1px; border-color: rgba(232,190,78,.3); transition: background-color .3s cubic-bezier(.22,.61,.36,1), border-color .3s cubic-bezier(.22,.61,.36,1); }
 .s-promise:hover .s-promise-ico .elementor-icon { background-color: rgba(232,190,78,.22) !important; border-color: rgba(232,190,78,.55); }
 .s-promise::after {
  content: ""; position: absolute; left: 0; bottom: -1px; width: 100%; height: 1px;
  background: #E8BE4E; transform: scaleX(0); transform-origin: left;
  transition: transform .4s cubic-bezier(.16,1,.3,1);
}
 .s-promise:hover::after { transform: scaleX(1); }

@media (max-width: 560px) {
   .s-promise { flex-direction: column; gap: 16px; }
   .s-promise-body { width: 100% !important; }
   .s-promise-ico .elementor-icon { padding: 10.5px !important; border-radius: 12px !important; }
   .s-promise-h3 { margin-top: 0 !important; }
}

 .s-rev { border-top-color: var(--e-global-color-primary) !important; }

 .s-shead-lead .tlink {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: "Roboto", system-ui, sans-serif; font-weight: 900; font-size: 13px;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--e-global-color-primary); text-decoration: none; margin-top: 16px;
}
 .s-shead-lead .tlink::after {
  content: ""; width: 17px; height: 17px; flex: 0 0 auto;
  background-color: currentColor;
  -webkit-mask: url("/wp-content/uploads/2026/08/icon-arrow-right-22.svg") no-repeat center / contain;
          mask: url("/wp-content/uploads/2026/08/icon-arrow-right-22.svg") no-repeat center / contain;
  transition: transform .22s cubic-bezier(.22,.61,.36,1);
}
 .s-shead-lead .tlink:hover::after { transform: translateX(5px); }

 .s-cta { position: relative; isolation: isolate; }
 .s-cta .s-cta-photo img { object-position: 50% 42% !important; }

 .s-cta-photo::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(100deg, rgb(0 39 229 / 68%) 0%, rgb(0 39 229 / 75%) 42%, rgb(0 0 0 / 76%) 72%, rgb(4 10 32 / 54%) 100%),
    linear-gradient(0deg, rgb(4 10 32 / 36%) 0%, rgb(4 10 32 / 46%) 58%);
}
 .s-cta::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(100deg,rgba(4,10,32,.64) 0%,rgba(4,10,32,.54) 34%,rgba(4,10,32,.22) 54%,rgba(4,10,32,.04) 70%,rgba(4,10,32,0) 82%);
}
@media (max-width: 1080px) {
   .s-cta::after { background: linear-gradient(0deg,rgba(4,10,32,.55) 0%,rgba(4,10,32,0) 55%); }
}

 .s-cta-truck img { filter: drop-shadow(0 30px 50px rgba(0,0,0,.45)); }
@media (min-width: 1081px) {
   .s-cta-truck {
    position: absolute; right: clamp(-70px,-2.5vw,-16px); bottom: 15px; left: auto; top: auto;
  }
}
/* ==========================================================================
   PAGE: about-us (post 102) — rung-(d) CSS
   Written 2026-08-17 by html-to-native-elementor.

   SCOPING RULE used here:
   * The four components that about-us introduces (pagehero / docsec+split /
     vm2 / team) are ALSO used by ceiling-fans.html and services.html, so they
     are written UNSCOPED as component blocks keyed on their own .s-* classes.
     Page 51 carries none of those classes, so nothing on the homepage can
     regress. Verified by grep after writing.
   * The three components about-us REUSES from page 51 (about / pledge / cta)
     need rules page 51 already owns under an `` prefix. Those are
     re-stated below prefixed `.elementor-102`. That is real duplication and is
     reported; see the de-duplication note at the end of this file.
   ========================================================================== */

/* --- a1 -------------------------------------------------------------------
   MISSING CONTROL: three-stop gradient overlay.
   Source: .pagehero__bg::after
     linear-gradient(180deg,rgba(4,10,32,.42) 0%,rgba(4,10,32,.58) 46%,rgba(4,10,32,.92) 100%)
   The Container's background-overlay gradient control exposes exactly TWO
   colours and two locations (background_overlay_color / _color_b /
   _gradient_position). A third stop cannot be expressed, and the middle stop
   at 46% is what keeps the headline legible without crushing the photo.
   -------------------------------------------------------------------------- */
.s-pagehero-bg::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(4,10,32,.42) 0%, rgba(4,10,32,.58) 46%, rgba(4,10,32,.92) 100%);
}

/* --- a2 -------------------------------------------------------------------
   MISSING CONTROL: arbitrary object-position on the Image widget.
   Source: .pagehero__bg img { object-position: 50% 38% }
   Elementor's object-position control is a SELECT of nine keyword pairs
   (center center, center left, ...); 38% is not expressible. !important is
   required because the widget always emits its own value at (0,4,0).
   -------------------------------------------------------------------------- */
.s-pagehero-photo img { object-position: 50% 38% !important; }

/* --- a3 -------------------------------------------------------------------
   MISSING CONTROL: styling one inline run inside a Heading.
   Source: .pagehero__title em { font-style: normal; color: var(--gold-2) }
   The three highlighted runs stay as literal <em> inside the Heading widget's
   own `title` field so the client still edits ONE string in the UI; no
   Elementor control can colour part of a heading.
   -------------------------------------------------------------------------- */
.s-pagehero-title em { font-style: normal; color: var(--e-global-color-accent); }

/* --- b1 : docsec / split media ------------------------------------------- NEW COMPONENT
   MISSING CONTROL: "fill the container" on the Video widget.
   Source: .split{align-items:stretch} makes .split__media exactly as tall as the sibling
   text column (measured 308.6px at 1920, where a true 16:9 of 553px would be 311.1px), and
   .split__media iframe{position:absolute;inset:0} fills it. Elementor's Video widget offers
   only a fixed aspect_ratio select (16:9 / 21:9 / 4:3 / 3:2 / 1:1 / 9:16) implemented as
   `height:0; padding-bottom: calc(100%/var(--video-aspect-ratio))`. There is no control that
   says "match my parent's height", so the fixed ratio has to be switched off here.

   CONTRACT (Elementor 4.2, differs from every 3.x write-up): the Video widget renders
     .elementor-widget-video > .elementor-wrapper > iframe.elementor-video
   There is NO .elementor-widget-container and NO .elementor-fit-aspect-ratio padding hack;
   the ratio is a real `aspect-ratio` on the iframe. Rules written against the 3.x markup
   match nothing and fail silently (they cost one build cycle here: the media column stayed
   at 16:9 of its own width - 311.0px instead of the 308.6px text height at 1920, and 455.6px
   instead of 360px at 900).
   Lifting .elementor-wrapper out of flow is what makes the column take its height from the
   sibling text column, exactly as .split__media iframe{position:absolute;inset:0} does.

   CONTRACT 2: every Elementor widget carries an identity `transform: matrix(1,0,0,1,0,0)`
   (it is how the transform controls are wired). A non-`none` transform makes the widget a
   containing block for absolutely positioned descendants, so an `inset:0` wrapper resolves
   against the WIDGET, not against the section container - and the widget's own auto height
   is 0 once its only child is out of flow.
   CONTRACT 3: `height:100%` on the widget is NOT a valid answer either. A percentage height
   resolves against the containing block's `height` PROPERTY, and at <=900 the media column
   sizes itself from `min-height` with height:auto - so height:100% resolved to 0 and the
   iframe vanished at 900/640 while working at 1920. Taking the widget out of flow with
   `inset:0` sizes it from the containing block's used padding box instead, which is correct
   in both regimes (stretch at >=1020, min-height at <=900). This is also exactly the
   mechanism the source uses: .split__media{position:relative} + iframe{position:absolute;inset:0}.
   -------------------------------------------------------------------------- */
.s-split-media .elementor-widget-video { position: absolute; inset: 0; }
.s-split-media .elementor-wrapper {
  position: absolute; inset: 0; aspect-ratio: auto !important;
}
.s-split-media iframe.elementor-video {
  position: absolute; inset: 0;
  width: 100% !important; height: 100% !important; border: 0;
}

/* --- b2 : vision/mission card at <=480 ----------------------------------- NEW COMPONENT
   Source: @media (max-width:480px){ .vm2__card{grid-template-columns:1fr}
                                     .vm2__ico{margin-bottom:14px} }
   480px is the template's 13th breakpoint. Elementor's five custom breakpoint slots are all
   spent (1180/1080/1020/900/640), so this reflow cannot be a responsive control.
   -------------------------------------------------------------------------- */
@media (max-width: 480px) {
  .s-vm2-card { flex-direction: column !important; }
  .s-vm2-card > .s-vm2-ico { margin-bottom: 14px !important; }
  .s-vm2-card > .s-vm2-body { width: 100% !important; }
}

/* --- b3 : vision/mission panel at <=860 ---------------------------------- NEW COMPONENT
   Source: @media (max-width:860px){ .vm2__grid{grid-template-columns:1fr} }
   Same reason as b2 - no breakpoint slot left at 860px.
   -------------------------------------------------------------------------- */
@media (max-width: 860px) {
  .s-vm2 > .s-vm2-card { width: 100% !important; }
}

/* --- b4 : team photo frame ----------------------------------------------- NEW COMPONENT
   MISSING CONTROL: aspect-ratio on a Container. Verified against the live control set for
   `container`: there is no aspect_ratio / ratio control of any kind (only min_height, which
   cannot be a function of the element's own width).
   Source: .tmember__photo{aspect-ratio:4/5}

   ...and the same CONTRACT 3 as the video above: the Image widget's own width/height 100%
   controls cannot resolve against an aspect-ratio-derived height, so the widget box grew to
   the photo's intrinsic ratio (284 x 367.6 instead of 284 x 355 for a 791x1024 portrait).
   The <img> still had object-fit:cover, so the box LOOKED right while cropping a different
   slice of the photo - 9 of 10 portraits mismatched and the one whose file happens to be
   4:5 (819x1024) matched perfectly, which is what proved the cause. Taking the widget out
   of flow sizes it from the container's used padding box.
   -------------------------------------------------------------------------- */
.s-tmember-photo { aspect-ratio: 4 / 5; isolation: isolate; }
.s-tmember-photo > .s-tmember-img { position: absolute; inset: 0; }
.s-tmember-photo > .s-tmember-img img { height: 100%; }

/* --- b5 : team photo hover bar ------------------------------------------- NEW COMPONENT
   MISSING CONTROL: a pseudo-element that wipes in on hover.
   Source: .tmember__photo::after{...transform:scaleX(0)} + .tmember:hover ...{scaleX(1)}
   Elementor's hover_animation presets are whole-element transforms; none of them draws a
   3px bar that grows from the left edge.
   -------------------------------------------------------------------------- */
.s-tmember-photo::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px; z-index: 2;
  background: var(--e-global-color-accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform .3s cubic-bezier(.16,1,.3,1);
  pointer-events: none;
}
.s-tmember:hover .s-tmember-photo::after { transform: scaleX(1); }

/* --- b6 : team grid, the 1021-1024px band -------------------------------- NEW COMPONENT
   Source: @media (max-width:1024px){ .team__grid{grid-template-columns:repeat(3,1fr)} }
   Elementor's `tablet` slot is 1020px, so the four widths 1021-1024 would keep FOUR columns
   while the source already shows three. The Elementor controls carry the 3-up width from
   1020 down; this rule only patches the 4px band above it. Bounded below at 641 so the
   2-up mobile width is not overridden.
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) and (min-width: 641px) {
  .s-team-grid > .s-tmember { width: calc((100% - 2 * clamp(18px,2vw,28px)) / 3) !important; }
}

/* ==========================================================================
   PAGE about-us (post 102): the three components REUSED from page 51.
   Every rule below is a verbatim re-scope of an `` rule that already
   exists earlier in this file, because page 51's page-level CSS was scoped by post
   ID rather than by component. See the DE-DUPLICATION note at the end of the file.
   ========================================================================== */

/* full-bleed cover art (pledge + cta background photos) — mirrors  */
.elementor-102 .s-fillphoto { position: absolute; inset: 0; z-index: 0; }
.elementor-102 .s-fillphoto .elementor-widget-container,
.elementor-102 .s-fillphoto figure { height: 100%; }

/* about — portrait frame, scrim and caption — mirrors  */
.elementor-102 .s-about-media { position: relative; }
.elementor-102 .s-about-media > .s-about-photo { position: absolute; inset: 0; }
.elementor-102 .s-about-media > .s-about-photo .elementor-widget-container,
.elementor-102 .s-about-media > .s-about-photo figure { height: 100%; }
.elementor-102 .s-about-media > .s-about-photo img { object-position: 42% 28% !important; }
@media (max-width: 1020px) {
  .elementor-102 .s-about-media > .s-about-photo img { object-position: 57% 28% !important; }
}
.elementor-102 .s-about-media::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 44%; z-index: 1;
  background: linear-gradient(0deg, rgba(15,18,22,.86) 0%, rgba(15,18,22,.5) 42%, rgba(15,18,22,0) 100%);
  pointer-events: none;
}
.elementor-102 .s-about-cap { z-index: 3; max-width: 60%; }
@media (max-width: 600px) {
  .elementor-102 .s-about-cap { max-width: 52% !important; }
}

/* shead stacks at 860 — mirrors  */
@media (max-width: 860px) {
  .elementor-102 .s-shead { flex-direction: column !important; align-items: flex-start !important; gap: 18px !important; }
  .elementor-102 .s-shead > .s-shead-main { width: 100% !important; }
}

/* pledge — mirrors  */
.elementor-102 .s-pledge { position: relative; isolation: isolate; }
.elementor-102 .s-pledge-photo { overflow: hidden; }
.elementor-102 .s-pledge .s-pledge-photo img { object-position: 64% 56% !important; }
.elementor-102 .s-pledge::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(100deg, rgba(0,14,51,.95) 0%, rgba(0,27,139,.86) 52%, rgba(0,39,229,.62) 100%);
}
.elementor-102 .s-pledge::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: url("/wp-content/uploads/2026/08/cta-bg-blue-1.jpg") center/cover no-repeat;
  opacity: .22; mix-blend-mode: overlay;
}
.elementor-102 .s-pledge-mark-sw12, .elementor-102 .s-pledge-mark-sw10 { display: none; }
@media (max-width: 900px) {
  .elementor-102 .s-pledge-mark-sw17 { display: none; }
  .elementor-102 .s-pledge-mark-sw12 { display: block; }
}
@media (max-width: 560px) {
  .elementor-102 .s-pledge-mark-sw12 { display: none; }
  .elementor-102 .s-pledge-mark-sw10 { display: block; }
  .elementor-102 .s-pledge-foot { width: fit-content !important; min-width: 100%; }
}
/* DELIBERATE DEVIATION FROM SOURCE, carried over from page 51 at the developer's request:
   the source's own pledge foot overflows the viewport at <=640 (documentElement.scrollWidth
   413 at a 375 viewport, on the SOURCE as well as the build) because its grid track's
   max-content minimum is the nowrap button. Page 51 was fixed by letting the button wrap;
   about-us does the same so the two pages behave identically on a phone. */
@media (max-width: 640px) {
  .elementor-102 .s-pledge-foot { width: 100% !important; min-width: 0 !important; }
  .elementor-102 .s-pledge-foot .elementor-button { white-space: normal !important; }
}

/* cta — mirrors  */
.elementor-102 .s-cta { position: relative; isolation: isolate; }
.elementor-102 .s-cta .s-cta-photo img { object-position: 50% 42% !important; }
.elementor-102 .s-cta-photo::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(100deg, rgb(0 39 229 / 68%) 0%, rgb(0 39 229 / 75%) 42%, rgb(0 0 0 / 76%) 72%, rgb(4 10 32 / 54%) 100%),
    linear-gradient(0deg, rgb(4 10 32 / 36%) 0%, rgb(4 10 32 / 46%) 58%);
}
.elementor-102 .s-cta::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(100deg,rgba(4,10,32,.64) 0%,rgba(4,10,32,.54) 34%,rgba(4,10,32,.22) 54%,rgba(4,10,32,.04) 70%,rgba(4,10,32,0) 82%);
}
@media (max-width: 1080px) {
  .elementor-102 .s-cta::after { background: linear-gradient(0deg,rgba(4,10,32,.55) 0%,rgba(4,10,32,0) 55%); }
}
.elementor-102 .s-cta-truck img { filter: drop-shadow(0 30px 50px rgba(0,0,0,.45)); }
@media (min-width: 1081px) {
  .elementor-102 .s-cta-truck {
    position: absolute; right: clamp(-70px,-2.5vw,-16px); bottom: 15px; left: auto; top: auto;
  }
}

/* ==========================================================================
   DE-DUPLICATION NOTE (about / pledge / cta)
   The `.elementor-102` block above is a re-scope, not new design. It exists because page
   51's rung-(d) CSS was scoped by POST ID (it originally lived in post-51.css), so the
   three shared components cannot be referenced from a second page without restating them.
   THE FIX, once ceiling-fans and services are built and all four pages can be re-scored in
   one pass: delete both the `` and `.elementor-102` prefixes from these
   blocks and keep ONE unscoped copy keyed on the component classes (.s-about-media,
   .s-pledge*, .s-cta*, .s-shead, .s-fillphoto). It was not done in this session because it
   changes rendering on the locked homepage and would require re-scoring all 12 of its
   sections. Nothing in these blocks is page-specific.
   ========================================================================== */

/* ==========================================================================
   PAGE: services (post 108) — rung-(d) CSS
   Written 2026-08-17 by html-to-native-elementor.

   SCOPING RULE used here (same as about-us, architecture.deviations[5]):
   * The ONE new component this page introduces, `.s-svcgrid`, needs NO CSS at
     all: the source's two reflows (.svcgrid 3->2 columns at max-width:1020 and
     2->1 at max-width:640) land exactly on Elementor's `tablet` and `mobile`
     breakpoint slots, so both are native responsive width controls.
   * The components this page REUSES from page 51 (scard / shead / rev / cta /
     fillphoto) need rules page 51 already owns under an `` prefix.
     Those are re-stated below prefixed `.elementor-108`. That is now the THIRD
     copy of the shead/cta rules and it grows the documented scoping debt; see
     the DE-DUPLICATION note at the end of this file and
     design-system/components.json -> css_scoping_debt.
   * The pagehero component's blocks (a1/a2/a3) and `.s-hazard` are already
     UNSCOPED earlier in this file, so this page reuses them with no new bytes.
   ========================================================================== */

/* --- s1 : .shead stacks at <=860 ----------------------------------------- mirrors 
   Source: @media (max-width:860px){ .shead{grid-template-columns:1fr;gap:18px;align-items:start} }
   860px is the template's 11th breakpoint. Elementor's five custom breakpoint slots are all
   spent (1180/1080/1020/900/640), so this reflow cannot be a responsive control.
   No `.s-shead-lead` clause here: neither section head on services.html has a lead paragraph.
   -------------------------------------------------------------------------- */
@media (max-width: 860px) {
  .elementor-108 .s-shead { flex-direction: column !important; align-items: flex-start !important; gap: 18px !important; }
  .elementor-108 .s-shead > .s-shead-main { width: 100% !important; }
}

/* --- s2 : full-bleed cover art ------------------------------------------- mirrors 
   MISSING CONTROL: "fill my parent" on the Image widget.
   Source: .scard__img / .cta__bg img { position:absolute; inset:0; width:100%; height:100% }
   The Image widget's own width/height 100% controls cannot resolve here: a percentage height
   resolves against the containing block's height PROPERTY, and .s-scard is sized by min-height
   (height:auto), so height:100% computes to ZERO. Taking the widget out of flow sizes it from
   the container's used padding box instead. This is the project's percentage-height contract.

   NEW CONTRACT (Elementor 4.2): the `.elementor-widget-container` wrapper NO LONGER EXISTS -
   grep the rendered page and it appears ZERO times; the widget div holds its content directly.
   The Image widget also emits a BARE <img> with no <figure> unless a caption is set. Page 51's
   equivalent block carries `.s-fillphoto .elementor-widget-container, .s-fillphoto figure
   {height:100%}` and BOTH of those selectors match nothing on this install. They are therefore
   NOT restated here: `position:absolute;inset:0` gives the widget a definite height, and the
   Image widget's own width/height 100% controls (which target `{{WRAPPER}} img`) then resolve
   against it. Verified: svc scores 100.00 at ten of eleven widths without them.
   -------------------------------------------------------------------------- */
.elementor-108 .s-fillphoto { position: absolute; inset: 0; z-index: 0; }

/* --- s3 : service card panel scrim --------------------------------------- mirrors 
   MISSING CONTROL: a FIVE-stop gradient overlay.
   Source: .scard__panel { background: linear-gradient(0deg,
             rgba(4,10,32,.97) 0%, rgba(4,10,32,.93) 46%, rgba(4,10,32,.72) 74%,
             rgba(4,10,32,.30) 90%, rgba(4,10,32,0) 100%) }
   The Container's background-overlay gradient control exposes exactly TWO colours and two
   locations. The 74%/90% stops are what keeps the photo readable above the copy.
   -------------------------------------------------------------------------- */
.elementor-108 .s-scard-panel {
  background: linear-gradient(0deg,
    rgba(4,10,32,.97) 0%, rgba(4,10,32,.93) 46%,
    rgba(4,10,32,.72) 74%, rgba(4,10,32,.30) 90%, rgba(4,10,32,0) 100%);
}

/* --- s4 : service card description, two-line clamp ---------------------- mirrors 
   MISSING CONTROL: truncation by line count.
   Source: .scard__desc { display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
                          overflow:hidden }
   Nothing in the Text Editor widget truncates text; without this the nine descriptions run to
   4-6 lines each and every card panel is 40-90px too tall.
   The clamp goes on the `<p>`, which is the Text Editor's direct child in Elementor 4.2 - see
   the `.elementor-widget-container` contract in s2 above; page 51's copy of this rule also
   targets that wrapper, and that half of its selector list is dead.
   -------------------------------------------------------------------------- */
.elementor-108 .s-scard-desc p {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* --- s5 : service card hover behaviour ----------------------------------- mirrors 
   MISSING CONTROLS, three of them:
   * `.scard:hover .scard__img{transform:scale(1.06)}` — Elementor's hover_animation presets
     transform the WHOLE element, not a descendant image, and there is no "zoom my background
     photo on parent hover" control.
   * `.scard__link::after` — a 3px gold bar that wipes in from the left on hover. No control
     draws a pseudo-element.
   * `.scard__go svg{transform:translateX(5px)}` on hover — again a descendant, not the element.
   -------------------------------------------------------------------------- */
.elementor-108 .s-scard .s-fillphoto img { transition: transform .8s cubic-bezier(.16,1,.3,1); }
.elementor-108 .s-scard:hover .s-fillphoto img { transform: scale(1.06); }
.elementor-108 .s-scard::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px; z-index: 4;
  background: var(--e-global-color-accent); transform: scaleX(0); transform-origin: left;
  transition: transform .4s cubic-bezier(.16,1,.3,1);
}
.elementor-108 .s-scard:hover::after { transform: scaleX(1); }
.elementor-108 .s-scard-go-icon .elementor-icon { line-height: 0; display: block; transition: transform .3s cubic-bezier(.22,.61,.36,1); }
.elementor-108 .s-scard:hover .s-scard-go-icon .elementor-icon { transform: translateX(5px); }

/* --- s6 : review card blue top border ----------------------------------- mirrors 
   Source: .rev { border:1px solid var(--line); border-top:3px solid var(--blue) }
   The Container's NATIVE border control carries per-side WIDTHS but a single COLOUR, so the
   3px top edge has to be recoloured here. !important is required: Elementor writes the
   element's own border-color into post-108.css, which loads AFTER this file, and an
   equal-specificity rule therefore LOSES on source order (page-51 finding — it was worth
   +0.6 at 1020 and ~+0.2 at every other width).
   -------------------------------------------------------------------------- */
.elementor-108 .s-rev { border-top-color: var(--e-global-color-primary) !important; }

/* --- s7 : cta ------------------------------------------------------------ mirrors 
   `section.cta` is byte-identical between index.html and services.html, so every rule here is a
   verbatim re-scope. What each one is for:
   * position/isolation — `isolation` has no control, and the section needs its own stacking
     context so the hazard stripe, the photo, the truck and the two scrims order correctly.
   * object-position 50% 42% — Elementor's control is a SELECT of nine keyword pairs.
   * .s-cta-photo::after — TWO stacked gradients of 4 and 2 stops; the overlay control carries
     one gradient of two colours.
   * .s-cta::after — a five-stop gradient whose DIRECTION changes at 1080 in the source.
   * drop-shadow on the truck — no filter control on the Image widget.
   * the truck's absolute placement above 1080 — `_position` is NOT a responsive control, and
     the source moves the truck between absolute and normal flow at that width.
   -------------------------------------------------------------------------- */
.elementor-108 .s-cta { position: relative; isolation: isolate; }
.elementor-108 .s-cta .s-cta-photo img { object-position: 50% 42% !important; }
.elementor-108 .s-cta-photo::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(100deg, rgb(0 39 229 / 68%) 0%, rgb(0 39 229 / 75%) 42%, rgb(0 0 0 / 76%) 72%, rgb(4 10 32 / 54%) 100%),
    linear-gradient(0deg, rgb(4 10 32 / 36%) 0%, rgb(4 10 32 / 46%) 58%);
}
.elementor-108 .s-cta::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(100deg,rgba(4,10,32,.64) 0%,rgba(4,10,32,.54) 34%,rgba(4,10,32,.22) 54%,rgba(4,10,32,.04) 70%,rgba(4,10,32,0) 82%);
}
@media (max-width: 1080px) {
  .elementor-108 .s-cta::after { background: linear-gradient(0deg,rgba(4,10,32,.55) 0%,rgba(4,10,32,0) 55%); }
}
.elementor-108 .s-cta-truck img { filter: drop-shadow(0 30px 50px rgba(0,0,0,.45)); }
@media (min-width: 1081px) {
  .elementor-108 .s-cta-truck {
    position: absolute; right: clamp(-70px,-2.5vw,-16px); bottom: 15px; left: auto; top: auto;
  }
}

/* NOT WRITTEN, DELIBERATELY: the `@media (max-width:640px)` pledge-foot block that
    and .elementor-102 both carry (architecture.deviations[7]). services.html has
   no .pledge section, and the SOURCE does not overflow a phone viewport here —
   documentElement.scrollWidth is exactly 375 at a 375 viewport. There is nothing to suppress,
   so no equivalent block exists for post 108 and none is inherited (the existing ones are
   post-ID scoped precisely so that this stays true). */

/* ==========================================================================
   PAGE: ceiling-fans (post 110) — rung-(d) CSS
   Written 2026-08-17 by html-to-native-elementor.

   SCOPING RULE used here (architecture.deviations[5]):
   * The NEW components this page introduces (coverphoto / dochead / features /
     typegrid / docsec--dark / twocol + promises / risks / gallery / areas / faq /
     split media map) are written UNSCOPED as component blocks keyed on their own
     .s-* classes. Pages 51 / 102 / 108 and templates 87 / 88 carry NONE of those
     classes — verified by grep before writing — so nothing already built can
     regress, and a future page can reuse them without a fourth re-scope.
   * The components this page REUSES from page 51 (shead / rev / cta / fillphoto)
     need rules page 51 owns under an `` prefix. Those are re-stated
     below prefixed `.elementor-110`. That is now the FOURTH copy and it grows the
     documented scoping debt — see design-system/components.json ->
     css_scoping_debt and the DE-DUPLICATION note earlier in this file.
   * The pagehero blocks (a1/a2/a3) and `.s-hazard` are already UNSCOPED earlier
     in this file, so section 1 needed ZERO new bytes.
   ========================================================================== */

/* --- c1 : cover art that fills its container ----------------------------- NEW COMPONENT
   MISSING CONTROL: "fill my parent" on the Image widget.
   Source: `.split__media img`, `.typecard__img img`, `.gallery__item img` and
   `.docsec--dark__bg img` are all
     position:absolute; inset:0; width:100%; height:100%; object-fit:cover
   Elementor's Image widget has width/height controls, but they target
   `{{WRAPPER}} img` — they cannot take the WIDGET out of flow, and a percentage
   height cannot resolve at all inside a container sized by min-height or by
   aspect-ratio (it computes to 0). `inset:0` gives the widget two definite
   dimensions; the widget's own width/height 100% controls then size the <img>.
   CONTRACT: do NOT restate page 51's `.elementor-widget-container` / `figure`
   companions — Elementor 4.2 emits neither, so both selectors match nothing.
   -------------------------------------------------------------------------- */
.s-coverphoto { position: absolute; inset: 0; z-index: 0; }

/* --- c2 : the .features / .typegrid column bands ------------------------- NEW COMPONENT
   MISSING CONTROL: a breakpoint at 1024 and at 460.
   Source: .features and .typegrid are `repeat(5,1fr)` and switch to
     repeat(3,1fr) at max-width:1024,  repeat(2,1fr) at max-width:640,
     repeat(1,1fr) at max-width:460.
   Elementor's five custom breakpoint slots are ALL spent (1180/1080/1020/900/640),
   so 3-up is put on the `tablet` (<=1020) and `mobile_extra` (<=900) slots and 2-up
   on `mobile` (<=640). That leaves exactly two bands unreachable by any control:
     * 1021-1024px, which must be 3-up (1024 IS a scored width; measured on the
       source: .feature 293.9px wide, three per row);
     * <=460px, which must be 1-up.
   Bounded on both sides so the native slots are never overridden.
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) and (min-width: 1021px) {
  .s-features, .s-typegrid { --e-con-grid-template-columns: repeat(3, 1fr) !important; }
}
@media (max-width: 460px) {
  .s-features, .s-typegrid { --e-con-grid-template-columns: repeat(1, 1fr) !important; }
}

/* --- c3 : .feature / .typecard hover ------------------------------------- NEW COMPONENT
   MISSING CONTROL: a hover TRANSFORM on a Container, and any hover state that acts
   on a DESCENDANT.
   Source: .feature:hover / .typecard:hover { transform: translateY(-4px);
             box-shadow: 0 22px 46px -20px rgba(15,18,22,.26) }
           .typecard:hover .typecard__img img { transform: scale(1.06) }
   The Container exposes hover background / border / (no) transform, and nothing at
   all that reaches a child widget's <img>. The base box-shadow, the base border and
   both backgrounds ARE native controls and are set there, not here.
   -------------------------------------------------------------------------- */
.s-feature, .s-typecard {
  transition: transform .25s cubic-bezier(.22,.61,.36,1),
              box-shadow .25s cubic-bezier(.22,.61,.36,1);
}
.s-feature:hover, .s-typecard:hover {
  transform: translateY(-4px);
  box-shadow: 0 22px 46px -20px rgba(15,18,22,.26);
}
.s-typecard .s-coverphoto img { transition: transform .6s cubic-bezier(.16,1,.3,1); }
.s-typecard:hover .s-coverphoto img { transform: scale(1.06); }

/* --- c4 : .typecard / .gallery photo frames ------------------------------ NEW COMPONENT
   MISSING CONTROL: aspect-ratio on a Container.
   Source: .typecard__img { aspect-ratio:4/3 }  .gallery__item { aspect-ratio:4/3 }
   The Container exposes min-height and height but NOT aspect-ratio, and there is no
   control that ties a box's height to its own width. Measured: at 1920 the typecard
   frame is 229.2px wide (231.2 minus the card's 1px border on each side) and 171.9px
   tall = exactly 3/4 of that, so the ratio resolves against the frame's own width and
   the rule belongs on the inner frame rather than on the card.
   `position:relative` is not restated: every `.e-con` is already relatively positioned.
   -------------------------------------------------------------------------- */
.s-typecard-img, .s-gallery-item { aspect-ratio: 4 / 3; }

/* --- c6 : .docsec--dark backdrop ----------------------------------------- NEW COMPONENT
   THREE MISSING CONTROLS in one block.
   (1) `isolation:isolate` on the section. No control exists, and it is load-bearing:
       the backdrop layer is `z-index:-1` so that the wrap's content sits above it
       without any z-index of its own, and without a new stacking context that -1
       paints BELOW the section's own navy background instead of above it.
   (2) Source: .docsec--dark__bg::after stacks TWO gradients, the first with FOUR
       colour stops:
         linear-gradient(100deg, .97 0%, .94 40%, .90 68%, .87 100%),
         linear-gradient(0deg,  .5 0%,  0 55%)
       The Container's background-overlay control carries exactly TWO colours and one
       position, and cannot stack two layers at all.
   (3) Source: .docsec--dark__bg img { object-position: 50% 46% }. The Image widget's
       object-position control is a SELECT of nine keyword pairs; 46% is not one.
       !important is required because the widget always emits its own value at (0,4,0).
   -------------------------------------------------------------------------- */
.s-whychoose { isolation: isolate; }
.s-dark-bg::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(100deg, rgba(4,10,32,.97) 0%, rgba(4,10,32,.94) 40%, rgba(4,10,32,.90) 68%, rgba(4,10,32,.87) 100%),
    linear-gradient(0deg, rgba(4,10,32,.5) 0%, rgba(4,10,32,0) 55%);
}
.s-dark-photo img { object-position: 50% 46% !important; }

/* --- c7 : .twocol collapses at <=860 ------------------------------------- NEW COMPONENT
   MISSING CONTROL: a breakpoint at 860.
   Source: @media (max-width:860px){ .twocol{grid-template-columns:1fr}
                                     .twocol__col{height:auto}
                                     .twocol__foot{margin-top:20px} }
   860px is the template's 11th breakpoint; Elementor's five custom slots are all spent
   (1180/1080/1020/900/640). 860 IS a scored width, so this is not optional.
   `height:auto` needs no counterpart: the built column takes its height from the grid's
   align-items:stretch, which a one-column grid resolves to the row's own content height.
   -------------------------------------------------------------------------- */
@media (max-width: 860px) {
  .s-twocol { --e-con-grid-template-columns: 1fr !important; }
  .s-twocol-foot { margin-top: 20px !important; }
}

/* --- c8 : .promise icon chrome, hover, and the <=560 reflow --------------- NEW COMPONENT
   MISSING CONTROLS:
   (1) The Icon widget in `view:stacked` exposes primary_color (background-color) and
       secondary_color (color + svg fill) but NO border-style and NO border-colour
       control, and its `border_width` control is CONDITIONED ON `view:framed`, so in
       stacked view it emits nothing either. The measured consequence of relying on it:
       `border-width` fell back to the CSS initial `medium` = 3px and the icon box came
       out 60x60 against the source's 56x56, pushing the whole promise body 4px right.
       All three of style, width and colour therefore have to come from here. The source needs border rgba(232,190,78,.3)
       while the glyph stroke stays full gold-2, so `view:framed` cannot express it
       either — framed drives both from primary_color.
   (2) `.promise::after` is a 1px gold bar that scaleX(0)->scaleX(1) on row hover, and
       `.promise:hover .promise__ico` restyles a DESCENDANT. Neither is reachable from a
       Container hover control.
   (3) Source: @media (max-width:560px){ .promise{grid-template-columns:1fr;gap:16px}
                 .promise__ico{width:48px;height:48px;border-radius:12px}
                 .promise__body h3{margin-top:0} }
       560px is not an Elementor slot, and 480 and 375 are both scored widths. The 48px
       box is 25px glyph + 2*10.5px padding + 2*1px border; `icon_padding` and
       `border_radius` on the Icon widget are NOT responsive controls, so both have to
       come from here.
   -------------------------------------------------------------------------- */
.s-promise-ico .elementor-icon {
  border-style: solid; border-width: 1px; border-color: rgba(232,190,78,.3);
}
.s-promise-ico .elementor-icon {
  transition: background-color .3s cubic-bezier(.22,.61,.36,1),
              border-color .3s cubic-bezier(.22,.61,.36,1);
}
/* !important on the BACKGROUND only, and it is load-bearing: the Icon widget's own
   `primary_color` control writes `background-color` into post-110.css under the selector
   `.elementor-110 .elementor-element-<id> .elementor-icon`, which TIES this rule on
   specificity (0,3,0) and then wins on SOURCE ORDER because post-110.css is enqueued after
   this file. Without it the hover state measured rgba(232,190,78,0.13) - the resting value -
   i.e. the hover never applied. Same contract as `.s-rev{border-top-color}` on page 51.
   The border-color needs no !important: no control emits a border-colour for a stacked Icon,
   so nothing competes with it (verified: it reached rgba(232,190,78,.55) already).
   ZERO pixel cost, which is why no score caught this - shot.js freezes transitions and
   captures resting state. Found by asserting the hovered computed style against the source. */
.s-promise:hover .s-promise-ico .elementor-icon {
  background-color: rgba(232,190,78,.22) !important; border-color: rgba(232,190,78,.55);
}
.s-promise::after {
  content: ""; position: absolute; left: 0; bottom: -1px; width: 100%; height: 1px;
  background: var(--e-global-color-accent); transform: scaleX(0); transform-origin: left;
  transition: transform .4s cubic-bezier(.16,1,.3,1);
}
.s-promise:hover::after { transform: scaleX(1); }
@media (max-width: 560px) {
  .s-promise {
    --e-con-grid-template-columns: 1fr !important;
    --gap: 16px 16px !important; --row-gap: 16px !important; --column-gap: 16px !important;
  }
  .s-promise-ico .elementor-icon { padding: 10.5px !important; border-radius: 12px !important; }
  .s-promise-h { margin-top: 0 !important; }
}

/* NUMBERING NOTE: block id `c5` was reserved during planning for the .features/.typegrid
   column bands and then merged into c2 when both grids turned out to need the identical
   two media queries. There is deliberately no c5; the sequence continues at c9. */

/* --- c9 : .risks list markers + note <strong> ---------------------------- NEW COMPONENT
   MISSING CONTROLS, two of them:
   (1) NO CONTROL DRAWS A PSEUDO-ELEMENT. Source:
         .risks li::before{content:"";position:absolute;left:0;top:1px;width:22px;height:22px;
                           background:rgba(232,190,78,.22)}
         .risks li::after {content:"!";position:absolute;left:9px;top:0;
                           font-family:var(--ff-display);font-weight:900;font-size:14px;
                           line-height:22px;color:var(--ink)}
       The alternative inside Elementor is an Icon widget per row, which would need a
       three-column grid per item and a bespoke "!" SVG - i.e. more nodes, more settings and
       a raster that no longer matches a text glyph. The `<li>`s are Text Editor widgets so
       the copy stays one editable field per risk.
       `position:relative` is not needed: every Elementor widget carries an identity
       `transform`, so the widget is already the containing block for its own absolute
       descendants (registered contract).
   (2) `.docsec__note strong{color:var(--ink)}` and `.risks li strong{color:var(--ink);
       font-weight:700}` restyle a DESCENDANT of a Text Editor. The widget's Text Color
       control sets the element's own colour only. font-weight:700 needs no rule - Chrome
       resolves `strong`'s default `bolder` to 700 against a 400 base - so only the colour
       is restated. This is the ONLY section on the page whose note contains <strong>
       (verified across all eleven sections), so the rule is scoped to the risks list and to
       `.s-note`, not applied to every Text Editor.
   -------------------------------------------------------------------------- */
.s-risk::before {
  content: ""; position: absolute; left: 0; top: 1px; width: 22px; height: 22px;
  background: rgba(232,190,78,.22);
}
.s-risk::after {
  content: "!"; position: absolute; left: 9px; top: 0;
  font-family: "Roboto", system-ui, sans-serif; font-weight: 900; font-size: 14px;
  line-height: 22px; color: var(--e-global-color-ink);
}
.s-risk strong, .s-guide .s-note strong { color: var(--e-global-color-ink); }

/* --- c10 : revs transplant ------------------------------------------------ mirrors 
   `section.revs` is BYTE-IDENTICAL between index.html and ceiling-fans.html, so every rule
   here is a verbatim re-scope of one page 51 already owns. FOURTH copy of the shead/rev
   rules - see design-system/components.json -> css_scoping_debt.

   MISSING CONTROLS:
   (1) A BREAKPOINT AT 860. Source:
         @media (max-width:860px){ .shead{grid-template-columns:1fr;gap:18px;align-items:start}
                                   .shead__lead{text-align:left} }
       Elementor's five custom slots are all spent (1180/1080/1020/900/640) and 860 IS a
       scored width. UNLIKE `.elementor-108`, this page's section head DOES carry a
       `.shead__lead`, so its clause is restated here as well.
   (2) The Container's NATIVE border control carries per-side WIDTHS but ONE colour, and the
       source is `.rev{border:1px solid var(--line); border-top:3px solid var(--blue)}`.
       `!important` is required: Elementor writes the element's own border-color into
       post-110.css, which loads AFTER this file, so an equal-specificity rule loses on
       source order (page-51 finding, worth ~+0.6 at 1020).
   (3) `.shead__lead .tlink` is an INLINE LINK INSIDE a paragraph. Elementor has no widget for
       "a styled link in the middle of a Text Editor", and splitting the paragraph into
       Text Editor + Button would change the source's inline flow and its wrap point. The
       arrow is the source's own inline <svg>, reproduced as a currentColor mask so the
       hover translate applies to the glyph exactly as `.tlink:hover svg` does.
   -------------------------------------------------------------------------- */
@media (max-width: 860px) {
  .elementor-110 .s-shead { flex-direction: column !important; align-items: flex-start !important; gap: 18px !important; }
  .elementor-110 .s-shead > .s-shead-main { width: 100% !important; }
  .elementor-110 .s-shead > .s-shead-lead { width: 100% !important; align-self: flex-start !important; text-align: left !important; }
}

.elementor-110 .s-rev { border-top-color: var(--e-global-color-primary) !important; }

.elementor-110 .s-shead-lead .tlink {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: "Roboto", system-ui, sans-serif; font-weight: 900; font-size: 13px;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--e-global-color-primary); text-decoration: none; margin-top: 16px;
}
.elementor-110 .s-shead-lead .tlink::after {
  content: ""; width: 17px; height: 17px; flex: 0 0 auto;
  background-color: currentColor;
  -webkit-mask: url("/wp-content/uploads/2026/08/icon-arrow-right-22.svg") no-repeat center / contain;
          mask: url("/wp-content/uploads/2026/08/icon-arrow-right-22.svg") no-repeat center / contain;
  transition: transform .22s cubic-bezier(.22,.61,.36,1);
}
.elementor-110 .s-shead-lead .tlink:hover::after { transform: translateX(5px); }

/* --- c11 : .gallery column bands + tile hover ----------------------------- NEW COMPONENT
   MISSING CONTROLS:
   (1) TWO BREAKPOINTS THAT DO NOT EXIST. Source:
         .gallery{grid-template-columns:repeat(3,1fr)}
         @media (max-width:760px){ .gallery{grid-template-columns:1fr 1fr} }
         @media (max-width:480px){ .gallery{grid-template-columns:1fr} }
       Elementor's five custom slots are all spent (1180/1080/1020/900/640). `mobile` (<=640)
       natively carries the 2-column value, which is correct for 481-640, but the source wants
       2 columns from 641 to 760 as well - and `mobile_extra` spans 641-900, which must stay
       3-up. Hence a MIN-WIDTH-BOUNDED band rather than a plain max-width query, so it cannot
       leak upward into 761-900. The <=480 band is not optional either: 480 and 375 are both
       scored widths.
   (2) `.gallery__item:hover img{transform:scale(1.05)}` scales a DESCENDANT on the parent's
       hover. Elementor's hover_animation presets transform the element itself, and the Image
       widget's own hover controls cannot be driven by an ancestor's hover state.
   The 4/3 frame and the absolutely-positioned photo are block c4 and block c1, already above.
   -------------------------------------------------------------------------- */
@media (max-width: 760px) and (min-width: 641px) {
  .s-gallery { --e-con-grid-template-columns: 1fr 1fr !important; }
}
@media (max-width: 480px) {
  .s-gallery { --e-con-grid-template-columns: 1fr !important; }
}
.s-gallery-item .s-coverphoto img { transition: transform .6s cubic-bezier(.16,1,.3,1); }
.s-gallery-item:hover .s-coverphoto img { transform: scale(1.05); }

/* --- c12 : cta transplant ------------------------------------------------- mirrors 
   `section.cta` differs from index.html's in exactly TWO strings (the kicker and the
   service-area sentence), so every rule here is a verbatim re-scope of one page 51 owns, and
   is identical in content to `.elementor-108`'s s7. FOURTH copy - see
   design-system/components.json -> css_scoping_debt.

   MISSING CONTROLS, one per rule:
   * `isolation` has no control at all, and it is load-bearing: the section needs its own
     stacking context so the hazard stripe, the photo, the truck and the two scrims order
     correctly.
   * `object-position: 50% 42%` - the Image widget's control is a SELECT of nine keyword pairs.
   * `.cta__bg::after` is TWO STACKED gradients, of four and two stops. The Container's
     background-overlay control carries ONE gradient of two colours.
   * `.cta::after` is a FIVE-stop gradient whose DIRECTION changes at 1080 in the source.
   * the truck's `drop-shadow()` - the Image widget has no filter control.
   * the truck moves between absolute and normal flow at 1080, and `_position` is NOT a
     responsive control.
   * `.s-fillphoto{position:absolute;inset:0}` - "fill my parent" does not exist on the Image
     widget, and a percentage height cannot resolve inside a min-height-sized box. Page 51's
     copy also carries `.elementor-widget-container` and `figure` selector halves; BOTH match
     nothing in Elementor 4.2 and are deliberately not restated (same call as .elementor-108).

   DELIBERATELY NOT WRITTEN: the `@media (max-width:640px)` pledge-foot block that
    and .elementor-102 carry (architecture.deviations[7]). ceiling-fans.html has
   NO .pledge section, so there is nothing for it to act on, and the two existing blocks are
   post-ID scoped so nothing is inherited. Expected consequence, measured on services:
   cta@375 reads 99.00 (the source's own financing button overflows its 335px container and
   `.cta{overflow:hidden}` clips the right pill cap). A reading of 97.93 would mean a
   deviation had leaked in.
   -------------------------------------------------------------------------- */
.elementor-110 .s-fillphoto { position: absolute; inset: 0; z-index: 0; }
.elementor-110 .s-cta { position: relative; isolation: isolate; }
.elementor-110 .s-cta .s-cta-photo img { object-position: 50% 42% !important; }
.elementor-110 .s-cta-photo::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(100deg, rgb(0 39 229 / 68%) 0%, rgb(0 39 229 / 75%) 42%, rgb(0 0 0 / 76%) 72%, rgb(4 10 32 / 54%) 100%),
    linear-gradient(0deg, rgb(4 10 32 / 36%) 0%, rgb(4 10 32 / 46%) 58%);
}
.elementor-110 .s-cta::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(100deg,rgba(4,10,32,.64) 0%,rgba(4,10,32,.54) 34%,rgba(4,10,32,.22) 54%,rgba(4,10,32,.04) 70%,rgba(4,10,32,0) 82%);
}
@media (max-width: 1080px) {
  .elementor-110 .s-cta::after { background: linear-gradient(0deg,rgba(4,10,32,.55) 0%,rgba(4,10,32,0) 55%); }
}
.elementor-110 .s-cta-truck img { filter: drop-shadow(0 30px 50px rgba(0,0,0,.45)); }
@media (min-width: 1081px) {
  .elementor-110 .s-cta-truck {
    position: absolute; right: clamp(-70px,-2.5vw,-16px); bottom: 15px; left: auto; top: auto;
  }
}

/* --- c13 : FAQ summary gap ------------------------------------------------ NEW COMPONENT
   MISSING CONTROL: the Nested Accordion's `icon_spacing` control (which emits
   `--n-accordion-icon-gap`, the <summary>'s flex `gap`) is CONDITIONED ON
   `accordion_item_title_position_horizontal !== 'stretch'`
   (elementor/modules/nested-accordion/widgets/nested-accordion.php L651). `stretch` is the
   ONLY value that emits `justify-content: space-between`, which the source requires
   (`.faq__item summary{justify-content:space-between; gap:20px}`), so the two controls are
   mutually exclusive by design: choosing space-between silently disables the gap control and
   the summary falls back to the widget stylesheet's own 10px.

   MEASURED COST of the 10px fallback: at a 340 viewport the title header was 228px wide
   instead of 218, so question 5 ("Do you install smart ceiling fans?", max-content 223.8px)
   stayed on ONE line where the SOURCE wraps it to two - the faq section 837.6px against the
   source's 860.1px, and the document 14590 against 14612. Invisible at all eleven scored
   widths (faq is 100.00 with zero mismatched pixels at every one, before and after this
   rule), but it is a real 10px error in a flex container and it moves wrap points on narrow
   phones, which is exactly the class of thing a scored-width grid can miss.
   -------------------------------------------------------------------------- */
.s-faq { --n-accordion-icon-gap: 20px; }

/* ==========================================================================
   TEMPLATE: 404 (elementor_library post 147, type error-404) — rung-(d) CSS
   Written 2026-08-26. Composed from already-scored components; the ONLY scoped
   CSS this template needs is the .s-cta transplant copy below — the FIFTH copy
   of the shared .s-cta block (see components.json -> css_scoping_debt). Added
   per the developer's "add one scoped copy now" decision (2026-08-26); the
   consolidation of copies 1-5 into one unscoped block stays deferred.
   pagehero / s-404-body / the .s-areas pill row need ZERO css — they are styled
   entirely by Theme Style + per-widget settings + __globals__ refs.
   Rescoped verbatim from the  copy (lines 383-405).
   ========================================================================== */
.elementor-147 .s-cta { position: relative; isolation: isolate; }
.elementor-147 .s-cta .s-cta-photo img { object-position: 50% 42% !important; }

.elementor-147 .s-cta-photo::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(100deg, rgb(0 39 229 / 68%) 0%, rgb(0 39 229 / 75%) 42%, rgb(0 0 0 / 76%) 72%, rgb(4 10 32 / 54%) 100%),
    linear-gradient(0deg, rgb(4 10 32 / 36%) 0%, rgb(4 10 32 / 46%) 58%);
}
.elementor-147 .s-cta::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(100deg,rgba(4,10,32,.64) 0%,rgba(4,10,32,.54) 34%,rgba(4,10,32,.22) 54%,rgba(4,10,32,.04) 70%,rgba(4,10,32,0) 82%);
}
@media (max-width: 1080px) {
  .elementor-147 .s-cta::after { background: linear-gradient(0deg,rgba(4,10,32,.55) 0%,rgba(4,10,32,0) 55%); }
}
.elementor-147 .s-cta-truck img { filter: drop-shadow(0 30px 50px rgba(0,0,0,.45)); }
@media (min-width: 1081px) {
  .elementor-147 .s-cta-truck {
    position: absolute; right: clamp(-70px,-2.5vw,-16px); bottom: 15px; left: auto; top: auto;
  }
}

/* ==========================================================================
   TEMPLATES: Blog Archive (148) / Blog Single (149) / Search Results (150)
   + the Post Card loop-item (146) — rung-(d) CSS. Written 2026-08-26.

   SCOPING: follows the developer's "add one scoped copy now" decision recorded
   in the 404 block above (2026-08-26). Consolidation of the shared component
   blocks into one unscoped copy stays DEFERRED — see components.json ->
   css_scoping_debt. These are copies SIX, SEVEN and EIGHT of the shead/cta
   group, written as a single three-prefix selector list rather than three
   separate blocks so the debt grows in copies but not in duplicated text.

   The pagehero blocks (a1/a2/a3) and `.s-hazard` are already UNSCOPED earlier
   in this file, so all three templates reuse them with no new bytes.

   WHY `.s-fillphoto` IS HERE AND WAS NOT IN THE 404 BLOCK:
   measured on /category/safety/ before this block existed — the cta backdrop
   (`.s-fillphoto.s-cta-photo`) computed `position:relative` at 1905x1271 and
   drove the cta section to 2401px tall instead of ~600px, because a percentage
   height cannot resolve inside a container sized by padding. The 404 template
   (147) transplants the same cta and has no `.s-fillphoto` rule of its own, so
   it is very likely carrying this defect too — REPORTED, not fixed here (404
   was explicitly out of scope for this session).
   ========================================================================== */

/* --- t1 : .shead stacks at <=860 --------------------------- mirrors .elementor-108
   860px is the template's 11th breakpoint; Elementor's five custom slots are all
   spent (1180/1080/1020/900/640), so this reflow cannot be a responsive control.
   No `.s-shead-lead` clause: none of these three section heads carries a lead. */
@media (max-width: 860px) {
  .elementor-148 .s-shead,
  .elementor-149 .s-shead,
  .elementor-150 .s-shead { flex-direction: column !important; align-items: flex-start !important; gap: 18px !important; }
  .elementor-148 .s-shead > .s-shead-main,
  .elementor-149 .s-shead > .s-shead-main,
  .elementor-150 .s-shead > .s-shead-main { width: 100% !important; }
}

/* --- t2 : full-bleed cover art ----------------------------- mirrors .elementor-108
   MISSING CONTROL: "fill my parent" on the Image widget. A percentage height
   resolves against the containing block's height PROPERTY, and these sections are
   sized by padding/min-height (height:auto), so height:100% computes to ZERO —
   or, as measured here, the image stays in flow at its intrinsic ratio and
   inflates the section. Taking the widget out of flow sizes it from the used
   padding box instead. Elementor 4.2 emits no `.elementor-widget-container` and
   no <figure>, so those selector halves are deliberately NOT restated. */
.elementor-148 .s-fillphoto,
.elementor-149 .s-fillphoto,
.elementor-150 .s-fillphoto { position: absolute; inset: 0; z-index: 0; }

/* --- t3 : cta ---------------------------------------------- mirrors .elementor-108
   `section.cta` is transplanted verbatim, so every rule here is a re-scope:
   * position/isolation — `isolation` has no control, and the section needs its own
     stacking context so hazard / photo / truck / the two scrims order correctly.
   * object-position 50% 42% — Elementor's control is a SELECT of nine keywords.
   * .s-cta-photo::after — TWO stacked gradients of 4 and 2 stops; the overlay
     control carries one gradient of two colours.
   * .s-cta::after — a five-stop gradient whose DIRECTION changes at 1080.
   * drop-shadow on the truck — no filter control on the Image widget.
   * the truck's absolute placement above 1080 — `_position` is not responsive. */
.elementor-148 .s-cta,
.elementor-149 .s-cta,
.elementor-150 .s-cta { position: relative; isolation: isolate; }
.elementor-148 .s-cta .s-cta-photo img,
.elementor-149 .s-cta .s-cta-photo img,
.elementor-150 .s-cta .s-cta-photo img { object-position: 50% 42% !important; }
.elementor-148 .s-cta-photo::after,
.elementor-149 .s-cta-photo::after,
.elementor-150 .s-cta-photo::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(100deg, rgb(0 39 229 / 68%) 0%, rgb(0 39 229 / 75%) 42%, rgb(0 0 0 / 76%) 72%, rgb(4 10 32 / 54%) 100%),
    linear-gradient(0deg, rgb(4 10 32 / 36%) 0%, rgb(4 10 32 / 46%) 58%);
}
.elementor-148 .s-cta::after,
.elementor-149 .s-cta::after,
.elementor-150 .s-cta::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(100deg,rgba(4,10,32,.64) 0%,rgba(4,10,32,.54) 34%,rgba(4,10,32,.22) 54%,rgba(4,10,32,.04) 70%,rgba(4,10,32,0) 82%);
}
@media (max-width: 1080px) {
  .elementor-148 .s-cta::after,
  .elementor-149 .s-cta::after,
  .elementor-150 .s-cta::after { background: linear-gradient(0deg,rgba(4,10,32,.55) 0%,rgba(4,10,32,0) 55%); }
}
.elementor-148 .s-cta-truck img,
.elementor-149 .s-cta-truck img,
.elementor-150 .s-cta-truck img { filter: drop-shadow(0 30px 50px rgba(0,0,0,.45)); }
@media (min-width: 1081px) {
  .elementor-148 .s-cta-truck,
  .elementor-149 .s-cta-truck,
  .elementor-150 .s-cta-truck {
    position: absolute; right: clamp(-70px,-2.5vw,-16px); bottom: 15px; left: auto; top: auto;
  }
}

/* ==========================================================================
   POST CARD loop-item (elementor_library post 146) — rung-(d) CSS.
   ONE copy serves all three templates: Elementor wraps every loop item in
   `<div class="elementor elementor-146 ... e-loop-item e-loop-item-{ID}">`
   (verified in the rendered DOM), so this prefix follows the card wherever
   Loop Grid renders it — archive, search, and Single's related grid.
   Rescoped verbatim from the `.elementor-108` scard rules (blocks s2-s5).
   ========================================================================== */
.elementor-146 .s-fillphoto { position: absolute; inset: 0; z-index: 0; }

/* five-stop panel scrim — the overlay control carries two colours */
.elementor-146 .s-scard-panel {
  background: linear-gradient(0deg,
    rgba(4,10,32,.97) 0%, rgba(4,10,32,.93) 46%,
    rgba(4,10,32,.72) 74%, rgba(4,10,32,.30) 90%, rgba(4,10,32,0) 100%);
}

/* two-line clamp — nothing in the Text Editor truncates by line count.
   DIFFERS FROM THE STATIC CARD ON PAGES 51/108, and this is load-bearing: there the
   Text Editor's content is authored HTML and Elementor emits a <p>, so the clamp goes
   on `.s-scard-desc p`. Here the content comes from the `post-excerpt` DYNAMIC TAG,
   which Elementor injects as a BARE TEXT NODE with no <p> wrapper (verified in the
   rendered DOM), so a `p`-only selector matches nothing and the excerpt ran to five
   lines, inflating every card panel. Both selectors are listed so the card behaves
   whether its copy is static or dynamic. */
.elementor-146 .s-scard-desc,
.elementor-146 .s-scard-desc p {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* hover trio — all three act on a DESCENDANT or a pseudo-element, which
   `hover_animation` cannot target */
.elementor-146 .s-scard .s-fillphoto img { transition: transform .8s cubic-bezier(.16,1,.3,1); }
.elementor-146 .s-scard:hover .s-fillphoto img { transform: scale(1.06); }
.elementor-146 .s-scard::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px; z-index: 4;
  background: var(--e-global-color-accent); transform: scaleX(0); transform-origin: left;
  transition: transform .4s cubic-bezier(.16,1,.3,1);
}
.elementor-146 .s-scard:hover::after { transform: scaleX(1); }
.elementor-146 .s-scard-go-icon .elementor-icon { line-height: 0; display: block; transition: transform .3s cubic-bezier(.22,.61,.36,1); }
.elementor-146 .s-scard:hover .s-scard-go-icon .elementor-icon { transform: translateX(5px); }

/* Loop Grid stretches its grid items; the card must fill the cell so a row of
   cards with unequal copy still bottom-aligns its panels (the `equal_height`
   control stretches the ITEM wrapper, not our container inside it). */
.elementor-146.e-loop-item { display: flex; }
.elementor-146.e-loop-item > .s-scard { width: 100%; }

/* --- t4 : single / post meta row -------------------------------------------
   MISSING CONTROLS, verified by enumerating the Post Info widget's own control
   list (dev/execute-php over widgets_manager->get_controls()):
   * There is NO general `typography_*` group on `post-info` — only
     `icon_typography_*`. The item text therefore cannot be set to the design's
     labelsm scale through a control at all.
   * There is NO control that removes an item type's DEFAULT icon. Passing
     `selected_icon: {value:"", library:""}` in the repeater is ignored and the
     widget still emitted FontAwesome `e-fas-calendar` / user glyphs — a foreign
     icon language on this site, where every icon is a bespoke SVG attachment.
     The design has no date/author element at all, so the faithful choice is no
     icon rather than a stand-in from another set.
   -------------------------------------------------------------------------- */
.elementor-149 .s-postmeta .elementor-icon-list-icon { display: none !important; }
.elementor-149 .s-postmeta .elementor-icon-list-item,
.elementor-149 .s-postmeta .elementor-icon-list-item a {
  font-family: Roboto, sans-serif; font-weight: 900; font-size: 11px;
  line-height: 1.5; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--e-global-color-paper);
}
.elementor-149 .s-postmeta .elementor-icon-list-item a:hover { color: var(--e-global-color-accent); }
/* the widget's `divider` control draws its separator with a border-left on the
   inline item; recolour it to the hero's on-dark palette */
.elementor-149 .s-postmeta .elementor-inline-item:not(:first-child)::before { border-color: rgba(255,255,255,.45); }

/* --- t5 : single / article content (post 149) --------------------------------
   The editor emits bare <p>/<h2>/<h3>/<ul>/<blockquote>, which nothing in this
   stylesheet or the Kit's Theme Style had ever targeted — so article prose was
   rendering at hello-elementor's defaults (h2 = Open Sans 500 32px) instead of
   the design system. Measured before this block, on /smoke-detector-safety/:
     h2 -> Open Sans 500 32px/38.4px #282828   (should be Roboto 900 uppercase)
     p  -> 15.5px/1.7 #282828, no measure cap  (~100ch at the 820px column)

   Everything below maps the article onto the SOURCE's own long-form system
   (assets/css/styles.css `.docsec` / `.dochead`), which is what every other
   interior page already uses, and is expressed through the Kit's global custom
   properties so a Kit change still propagates:
     .h2          -> --e-global-typography-primary  (Roboto 900 up, clamp 34-76)
     h3-h6        -> INHERITED from Kit 12 Theme Style (spacing set here only)
     .docsec__lead-> 15.5/1.7, --ink-600  (74ch cap dropped, see below)
     body copy    -> 15.5/1.7, --text  (peer `.s-guide-p` on /ceiling-fans/)
     (the .docsec+.docsec hairline was trialled above each h2 and REMOVED on
      developer direction 2026-08-31, along with the 74ch prose cap — paragraphs
      now run the full container width)
   Peer-verified against /ceiling-fans/ at 1920: h2 76px/71.44px/-2.66px #0f1216,
   prose 15.5px/26.35px, lead #4a4e55, body #282828.
   HARDENING (2026-08-31, from maint-qa pass 1): the two child-combinator rules
   below are duplicated for `> .elementor-widget-container >` because Elementor
   currently renders si000e WITHOUT that wrapper. If a future version restores
   it, the single-combinator form would silently stop matching and the lead
   paragraph would revert to body colour with no error surface.
   -------------------------------------------------------------------------- */

/* ---- flow container ---- */
.elementor-149 .s-post-content { color: var(--e-global-color-text); }
.elementor-149 .s-post-content > *:first-child,
.elementor-149 .s-post-content > .elementor-widget-container > *:first-child { margin-top: 0; border-top: 0; padding-top: 0; }
.elementor-149 .s-post-content > *:last-child,
.elementor-149 .s-post-content > .elementor-widget-container > *:last-child { margin-bottom: 0; }

/* ---- body copy — full container width (74ch cap removed 2026-08-31) ---- */
.elementor-149 .s-post-content p {
  font-family: var(--e-global-typography-text-font-family), sans-serif;
  font-size: var(--e-global-typography-text-font-size);
  font-weight: var(--e-global-typography-text-font-weight);
  line-height: var(--e-global-typography-text-line-height);
  color: var(--e-global-color-text);
  margin: 0 0 20px;
}
/* opening paragraph takes the site's `.docsec__lead` role */
.elementor-149 .s-post-content > p:first-of-type,
.elementor-149 .s-post-content > .elementor-widget-container > p:first-of-type { color: var(--e-global-color-ink600); }

.elementor-149 .s-post-content strong,
.elementor-149 .s-post-content b { color: var(--e-global-color-ink); font-weight: 700; }
.elementor-149 .s-post-content em { font-style: italic; }

/* ---- headings ----
   VERIFIED 2026-08-31 by probing bare <h3>-<h6> in this page context, outside
   .s-post-content so these rules could not apply: Kit 12's Theme Style already
   governs them site-wide and wins unaided —
     h3  Roboto 900 clamp(17px,1.45vw,21px) / 1.2  / -0.015em / uppercase #0f1216
     h4  Roboto 900 clamp(15px,1.2vw,17px)  / 1.25 / -0.01em  / uppercase #0f1216
     h5  Roboto 900 14px                    / 1.3  / -0.005em / uppercase #0f1216
     h6  Roboto 700 12px                    / 1.35 / +0.04em  / uppercase #0f1216
   — identical to every other Elementor page. This block therefore sets ONLY
   their SPACING and never their typography, so a Theme Style change still
   propagates. An earlier revision of t5 redeclared h5/h6 at 13px/900/0.16em in
   --ink-600, which silently diverged from the Theme Style; that is removed.

   h1 and h2 have NO Theme Style entry, so hello-elementor wins on them (h2 was
   rendering Open Sans 500 32px/38.4px). Those two are the only headings mapped
   explicitly here, onto the Kit's display presets. */
.elementor-149 .s-post-content h1 {
  font-family: var(--e-global-typography-secondary-font-family), sans-serif;
  font-size: var(--e-global-typography-secondary-font-size);
  font-weight: var(--e-global-typography-secondary-font-weight);
  line-height: var(--e-global-typography-secondary-line-height);
  letter-spacing: var(--e-global-typography-secondary-letter-spacing);
  text-transform: var(--e-global-typography-secondary-text-transform);
  color: var(--e-global-color-ink);
  margin: clamp(52px, 5.2vw, 84px) 0 18px;
}
.elementor-149 .s-post-content h2 {
  font-family: var(--e-global-typography-primary-font-family), sans-serif;
  font-size: var(--e-global-typography-primary-font-size);
  font-weight: var(--e-global-typography-primary-font-weight);
  line-height: var(--e-global-typography-primary-line-height);
  letter-spacing: var(--e-global-typography-primary-letter-spacing);
  text-transform: var(--e-global-typography-primary-text-transform);
  color: var(--e-global-color-ink);
  /* no rule above the heading — developer direction 2026-08-31. The 76px display
     heading is its own section break, so the separator was redundant. */
  margin: clamp(52px, 5.2vw, 84px) 0 18px;
}
/* spacing only — typography deliberately inherited from the Theme Style */
.elementor-149 .s-post-content h3 { margin: clamp(30px, 3vw, 40px) 0 10px; }
.elementor-149 .s-post-content h4 { margin: 26px 0 8px; }
.elementor-149 .s-post-content h5,
.elementor-149 .s-post-content h6 { margin: 22px 0 8px; }

/* ---- links — the source resets `a` to inherit/no-underline, but prose links
        must stay identifiable, so they take the brand blue + an underline ---- */
.elementor-149 .s-post-content a {
  color: var(--e-global-color-primary);
  text-decoration: underline;
  text-underline-offset: 0.14em;
  text-decoration-thickness: 1px;
  transition: color .2s cubic-bezier(.22,.61,.36,1);
}
.elementor-149 .s-post-content a:hover { color: var(--e-global-color-blue900); }

/* ---- lists — the source resets `ul` to list-style:none and draws its own
        markers, so editor lists get a bespoke gold marker in the same idiom ---- */
.elementor-149 .s-post-content ul,
.elementor-149 .s-post-content ol {
  margin: 0 0 20px; padding: 0; list-style: none;
}
.elementor-149 .s-post-content li {
  position: relative;
  padding-left: 26px;
  font-family: var(--e-global-typography-text-font-family), sans-serif;
  font-size: var(--e-global-typography-text-font-size);
  line-height: var(--e-global-typography-text-line-height);
  color: var(--e-global-color-text);
  margin-bottom: 9px;
}
.elementor-149 .s-post-content li:last-child { margin-bottom: 0; }
.elementor-149 .s-post-content ul > li::before {
  content: ""; position: absolute; left: 2px; top: calc(var(--e-global-typography-text-line-height) * 0.5em - 3.5px);
  width: 7px; height: 7px; border-radius: 1px; background: var(--e-global-color-accent);
}
.elementor-149 .s-post-content ol { counter-reset: s-post-ol; }
.elementor-149 .s-post-content ol > li { counter-increment: s-post-ol; padding-left: 30px; }
.elementor-149 .s-post-content ol > li::before {
  content: counter(s-post-ol) ".";
  position: absolute; left: 0; top: 0;
  font-family: var(--e-global-typography-h3disp-font-family), sans-serif;
  font-weight: 900; font-size: 13px; letter-spacing: 0.02em;
  line-height: calc(var(--e-global-typography-text-line-height) * 1em);
  color: var(--e-global-color-gold);
}
.elementor-149 .s-post-content li > ul,
.elementor-149 .s-post-content li > ol { margin: 9px 0 0; }

/* ---- blockquote ---- */
.elementor-149 .s-post-content blockquote {
  margin: clamp(28px, 3vw, 38px) 0;
  padding: 2px 0 2px clamp(20px, 2vw, 28px);
  border-left: 3px solid var(--e-global-color-accent);
  font-size: 17px; line-height: 1.6; color: var(--e-global-color-ink);
}
.elementor-149 .s-post-content blockquote p { font-size: inherit; line-height: inherit; color: inherit; max-width: none; }
.elementor-149 .s-post-content blockquote cite,
.elementor-149 .s-post-content figcaption {
  display: block; margin-top: 12px;
  font-family: var(--e-global-typography-h3disp-font-family), sans-serif;
  font-weight: 900; font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase;
  font-style: normal; color: var(--e-global-color-grey);
}

/* ---- media ---- */
.elementor-149 .s-post-content img { max-width: 100%; height: auto; border-radius: 12px; display: block; }
.elementor-149 .s-post-content figure { margin: clamp(28px, 3vw, 40px) 0; }
.elementor-149 .s-post-content .alignleft  { float: left;  margin: 6px clamp(20px,2vw,28px) 20px 0; max-width: 46%; }
.elementor-149 .s-post-content .alignright { float: right; margin: 6px 0 20px clamp(20px,2vw,28px); max-width: 46%; }
.elementor-149 .s-post-content .aligncenter { margin-inline: auto; }

/* ---- rules / tables / code ---- */
.elementor-149 .s-post-content hr {
  border: 0; border-top: 1px solid var(--e-global-color-line); margin: clamp(36px, 4vw, 56px) 0;
}
.elementor-149 .s-post-content table { width: 100%; border-collapse: collapse; margin: 0 0 20px; font-size: 15px; }
.elementor-149 .s-post-content th, .elementor-149 .s-post-content td {
  border-bottom: 1px solid var(--e-global-color-line); padding: 12px 14px; text-align: left;
}
.elementor-149 .s-post-content th {
  font-family: var(--e-global-typography-h3disp-font-family), sans-serif;
  font-weight: 900; font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--e-global-color-ink); background: var(--e-global-color-paper2);
}
.elementor-149 .s-post-content code,
.elementor-149 .s-post-content pre {
  background: var(--e-global-color-paper2); border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 14px;
}
.elementor-149 .s-post-content code { padding: 2px 6px; }
.elementor-149 .s-post-content pre { padding: 18px 20px; overflow-x: auto; margin: 0 0 20px; }
.elementor-149 .s-post-content pre code { padding: 0; background: none; }

/* ---- mobile: pull the h2 section rule in tighter ---- */
@media (max-width: 640px) {
  .elementor-149 .s-post-content h2 { margin-top: 40px; }
  .elementor-149 .s-post-content .alignleft,
  .elementor-149 .s-post-content .alignright { float: none; max-width: 100%; margin-inline: 0; }
}

/* --- t6 : Post Card (146) — non-post results in search -----------------------
   WordPress search returns PAGES as well as posts, and the Post Card is built
   for posts, so a page result rendered as a broken card: no featured image
   (pages have no thumbnail) and a category chip reading "News" — the literal
   fallback string baked into the post-terms tag on 2026-08-26.

   Fixed at the source in template 146: the featured-image tag now falls back to
   attachment 14, and the post-terms fallback is now an EMPTY string instead of
   "News". This block hides the chip entirely when that fallback resolves empty,
   so a page result shows a clean image + title + excerpt card rather than an
   empty gold pill. Scoped to the card class, so it applies wherever the card is
   looped — archive 148, search 150 and the blog page.
   -------------------------------------------------------------------------- */
.s-scard-tag:not(:has(.elementor-heading-title:not(:empty))) { display: none !important; }

/* Fallback for browsers without :has() — collapses the pill to nothing rather
   than hiding it, which degrades gracefully instead of leaving a gold block. */
@supports not selector(:has(*)) {
  .s-scard-tag .elementor-heading-title:empty { display: none; }
  .s-scard-tag:empty { display: none; }
}

/* --- t7 : CANONICAL .s-fillphoto / .s-cta — consolidation of the deferred debt ----
   PAYS DOWN the duplication recorded at line ~660 ("consolidation of copies 1-5
   still deferred") and at ~1230 ("147 transplants the same cta and has no
   .s-fillphoto rule of its own"). That note predicted exactly what happened when
   13 new service pages were built from the ceiling-fans template on 2026-08-31:
   none of them matched a `.elementor-<id>` prefix, so `.s-fillphoto` kept its
   default `position:static`, the cta backdrop sat IN FLOW at full intrinsic size,
   and the cta section measured 2463px against ceiling-fans' 586px.

   The per-page copies were compared before writing this block:
     .elementor-108 vs .elementor-110  -> IDENTICAL (8 rules)
      / .elementor-102    -> same 8 rules PLUS a `height:100%` on
                                          .s-fillphoto's container/figure
   So there is ONE real rule set, not five. It is written once here, unscoped, and
   includes the height:100% variant so the union is preserved. The older scoped
   copies are now redundant duplicates of these exact declarations - they do no
   harm while present, and are removed in a separate, separately-verified step.

   Applies to every page that uses the classes, including any built in future.
   -------------------------------------------------------------------------- */
.s-fillphoto { position: absolute; inset: 0; z-index: 0; }
.s-fillphoto .elementor-widget-container,
.s-fillphoto figure { height: 100%; }

.s-cta { position: relative; isolation: isolate; }
.s-cta .s-cta-photo img { object-position: 50% 42% !important; }
.s-cta-photo::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(100deg, rgb(0 39 229 / 68%) 0%, rgb(0 39 229 / 75%) 42%, rgb(0 0 0 / 76%) 72%, rgb(4 10 32 / 54%) 100%),
    linear-gradient(0deg, rgb(4 10 32 / 36%) 0%, rgb(4 10 32 / 46%) 58%);
}
.s-cta::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(100deg,rgba(4,10,32,.64) 0%,rgba(4,10,32,.54) 34%,rgba(4,10,32,.22) 54%,rgba(4,10,32,.04) 70%,rgba(4,10,32,0) 82%);
}
@media (max-width: 1080px) {
  .s-cta::after { background: linear-gradient(0deg,rgba(4,10,32,.55) 0%,rgba(4,10,32,0) 55%); }
}
.s-cta-truck img { filter: drop-shadow(0 30px 50px rgba(0,0,0,.45)); }
@media (min-width: 1081px) {
  .s-cta-truck {
    position: absolute; right: clamp(-70px,-2.5vw,-16px); bottom: 15px; left: auto; top: auto;
  }
}
/* card hover zoom, previously scoped to  / .elementor-108 only */
.s-scard .s-fillphoto img { transition: transform .8s cubic-bezier(.16,1,.3,1); }
.s-scard:hover .s-fillphoto img { transform: scale(1.06); }


/* ── t8 ── s-townlist: county service areas ───────────────────────────────
   Used by /service-areas/. Matches the LIVE page's arrangement, drawn with this
   site's tokens: the county name centred on its own line, then its towns
   flowing down FOUR columns, each marked with the site's own bolt icon.
   (Live builds exactly this with an Elementor icon-list split across four
   col-25 columns, `fas fa-bolt` on every item.)

   Two earlier attempts are recorded here because both failed for reasons worth
   not repeating: three narrow cards with <ul> bullets - the card body is centred
   by design, so `list-style-position:outside` marooned every marker ~40px from
   its own text; then full-width cards of bordered chips - 71 uppercase outlined
   pills read as noise, and neither version resembled the page being copied.

   `columns` (not grid) is deliberate: it flows top-to-bottom then across, which
   is the order live lists the towns in. Grid auto-flow would read across rows
   and scramble the alphabetical run.                                          */
.s-townlist .s-features {
  --e-con-grid-template-columns: 1fr !important;
  gap: clamp(40px, 5vw, 68px);
}

/* The county blocks are not cards on live. The panel itself (paper background,
   drop shadow, padding) is switched off in the ELEMENTOR settings by the
   generator's D22 `card_panel: false` - a native control cannot be out-specified
   from here without !important, since Elementor emits it at
   `.elementor-<page> .elementor-element.elementor-element-<id>`. Only the hover
   lift, which IS defined in this file (component c3), is cancelled here.      */
.s-townlist .s-feature:hover { transform: none; box-shadow: none; }

/* live has no icon above the county name */
.s-townlist .s-feature > .elementor-widget-icon { display: none; }

.s-townlist .s-feature .elementor-widget-heading .elementor-heading-title {
  text-align: center;
  font-family: "Roboto", sans-serif;
  font-weight: 900;
  font-size: clamp(18px, 1.6vw, 22px);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--e-global-color-text);
  padding-bottom: 14px;
  border-bottom: 1px solid rgba(15, 18, 22, .12);
  margin-bottom: 26px;
}

.s-townwrap {
  columns: 4;
  column-gap: clamp(20px, 2.4vw, 36px);
}

.s-townwrap .s-town {
  display: flex;
  align-items: center;
  gap: 10px;
  break-inside: avoid;
  padding: 5px 0;
  font-family: "Open Sans", sans-serif;
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--e-global-color-text);
}

.s-townwrap .s-town::before {
  content: "";
  flex: 0 0 18px;
  width: 18px;
  height: 18px;
  background-color: var(--e-global-color-primary);
  -webkit-mask: url("../../../uploads/2026/08/icon-feature-bolt.svg") center / contain no-repeat;
          mask: url("../../../uploads/2026/08/icon-feature-bolt.svg") center / contain no-repeat;
}

/* live's H2 here is an 89-character sentence; the global display scale rendered
   it at 76px over three lines and swamped the page. Capped for this section
   only - the TEXT is untouched. */
.s-townlist .s-dochead h2 {
  font-size: clamp(24px, 2.6vw, 38px) !important;
  line-height: 1.15 !important;
  letter-spacing: -0.01em !important;
  max-width: 32ch;
  margin-inline: auto;
}

@media (max-width: 1180px) { .s-townwrap { columns: 3; } }
@media (max-width: 900px)  { .s-townwrap { columns: 2; } }
@media (max-width: 520px)  {
  .s-townwrap { columns: 1; }
  .s-townwrap .s-town { font-size: 15px; }
}

/* ── t9 ── split media floor ───────────────────────────────────────────────
   `.s-split` stretches both columns to the taller of the two, so a section with
   SHORT copy squashes its own photo. Measured on the new code-violations
   sub-page: "Bad Splicing" is two sentences, and its 720x373 photo rendered
   554x97 - a sliver showing a crop of a junction box. Old Wiring 554x123,
   Wrong Circuit Breaker 554x150.

   A floor fixes it without touching anything already built: the reference page
   /services/ceiling-fans/ measures 313px and 378px for its two media columns,
   and every guide photo across the 21 earlier pages is taller still (322-556px),
   so 300px sits below all of them and only bites where a photo would otherwise
   collapse. Not applied under 901px, where the grid is a single column and the
   media column's own mobile min-height already governs.                       */
@media (min-width: 901px) {
  .s-split-media { min-height: 300px; }
}


/* ── t10 ── Gravity Forms, dressed as this site ────────────────────────────
   The contact form is Gravity Forms (live uses an Elementor Pro form; Gravity
   was the brief). Out of the box GF ships its "orbital" theme: #204ce5 button,
   3px radius, Open Sans 14px/500, sentence case - which reads as a bolted-on
   plugin beside this site's gold 999px Roboto-900 uppercase buttons.

   WHY !important HERE, when nothing else in this file needs it: GF prints an
   INLINE <style> whose selector is
       #gform_wrapper_1[data-form-index="0"].gform-theme { --gf-color-primary: ... }
   an ID + attribute + class (1,3,0). Every class-based override loses to it, and
   the form id is not stable enough to hardcode. GF's own framework sheet also
   loads after the theme's and resets padding/background through
   `.gform-theme--framework :where(...)`. This is a plugin-integration boundary,
   not a cascade this file authored, so the overrides are marked important
   deliberately and kept to the few properties that actually need it.

   Labels stay hidden (the form is configured hidden_label) because live's form
   carries its text in the placeholders, not in labels.                       */
.gform_wrapper.gform-theme {
  --gf-color-primary: var(--e-global-color-primary) !important;
  --gf-color-primary-contrast: #fff !important;
  --gf-color-in-ctrl-primary: var(--e-global-color-primary) !important;
  --gf-radius: 10px !important;
}

/* a single-column form should not run the full 1220px band */
.s-split-text .gform_wrapper { max-width: 760px; }
.gform_wrapper .gform_fields { row-gap: 18px; }

.gform_wrapper .gfield input[type="text"],
.gform_wrapper .gfield input[type="email"],
.gform_wrapper .gfield input[type="tel"],
.gform_wrapper .gfield textarea,
.gform_wrapper .gfield select {
  width: 100% !important;
  height: auto !important;
  padding: 14px 18px !important;
  border: 1px solid rgba(15, 18, 22, .18) !important;
  border-radius: 10px !important;
  background: #fff !important;
  font-family: "Open Sans", sans-serif !important;
  font-size: 15.5px !important;
  line-height: 1.5 !important;
  color: var(--e-global-color-text) !important;
  box-sizing: border-box;
}
.gform_wrapper .gfield textarea { min-height: 150px !important; resize: vertical; }

.gform_wrapper .gfield input:focus,
.gform_wrapper .gfield textarea:focus,
.gform_wrapper .gfield select:focus {
  outline: none !important;
  border-color: var(--e-global-color-primary) !important;
  box-shadow: 0 0 0 3px rgba(0, 39, 229, .12) !important;
}

/* the consent line is long legal text - keep it quiet */
.gform_wrapper .gfield--type-consent .gfield_consent_description,
.gform_wrapper .gfield--type-consent .gform-field-label--type-inline {
  font-family: "Open Sans", sans-serif !important;
  font-size: 12.5px !important;
  line-height: 1.6 !important;
  color: rgba(15, 18, 22, .62) !important;
}

/* SEND: the site's own button, values measured off .elementor-button */
.gform_wrapper .gform_footer button.gform_button,
.gform_wrapper .gform_footer input.gform_button {
  background: var(--e-global-color-accent, #E8BE4E) !important;
  color: #0F1216 !important;
  border: 0 !important;
  border-radius: 999px !important;
  padding: 15.5px 34px !important;
  width: auto !important;
  height: auto !important;
  font-family: "Roboto", sans-serif !important;
  font-weight: 900 !important;
  font-size: 14px !important;
  letter-spacing: 1.26px !important;
  text-transform: uppercase !important;
  cursor: pointer;
  transition: transform .2s cubic-bezier(.22,.61,.36,1), box-shadow .2s;
}
.gform_wrapper .gform_footer button.gform_button:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 26px -12px rgba(15, 18, 22, .45);
}

@media (max-width: 640px) {
  .gform_wrapper .gform_footer button.gform_button { width: 100% !important; }
}

/* ── t11 ── s-badgegrid: icon badges in the types card ─────────────────────
   Used by /safety-and-savings-plan/. That page's nine benefits are square
   transparent PNG badges, not photographs. The stock `.s-typecard-img` frame is
   `aspect-ratio: 4/3` with `object-fit: cover`, which is right for a photo and
   wrong here - it would crop ~25% off a 1080x1080 badge and butt it against the
   card edges.

   So for this variant the frame becomes a square, the image is CONTAINED rather
   than cropped, and it sits on the card's own surface with breathing room.     */
.s-badgegrid .s-typecard-img {
  aspect-ratio: 1 / 1;
  max-width: 148px;
  margin: 4px auto 0;
  padding: 10px;
  background: none;
  overflow: visible;
}
.s-badgegrid .s-typecard-img img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
/* the photo zoom-on-hover is a photograph affordance; a badge should sit still */
.s-badgegrid .s-typecard:hover .s-typecard-img img { transform: none; }

/* Centring is NOT done here: the heading and copy widgets each carry their own
   Elementor `align` control, which beats text-align inherited from this box. It
   is set on the widgets by the generator (D24) instead. Same for the card's
   padding - the template's card padding is 0, and the body's own padding never
   reached the photo frame.                                                    */
.s-badgegrid .s-typecard-body .elementor-widget-heading { margin-bottom: 8px; }

/* ── t12 ── s-financing: GreenSky merchant module column ───────────────────
   Used by /financing/. The right column holds GreenSky's own hosted module - a
   fixed 330px block of <img>/<a> served from greensky.com - not a photograph.
   The stock `.s-split-media` is a cropping photo frame: it paints a background,
   clips overflow, and (block t9) carries a 300px min-height. All three are wrong
   for a fixed-width third-party widget, so they are unset here.

   The module's own width is set inline by GreenSky at 330px; it is centred in
   the column rather than stretched, and scales down only if the column gets
   narrower than the module on small screens.                                 */
.s-financing .s-split-media {
  background: none;
  overflow: visible;
  min-height: 0;
  align-self: start;
  display: flex;
  justify-content: center;
}
.s-financing .financing-module { max-width: 100%; }
.s-financing .financing-module img { display: block; width: 100%; height: auto; }

/* live's copy carries its own <h3> inside the text; give it the site's rhythm */
.s-financing .s-split-text h3 {
  margin: 26px 0 10px;
}
