/* StepEdge design system — audit remediation 2026-08-26
   Every rule below traces to a defect measured on the live page.

   MEASUREMENT NOTE: Zion Builder paints a section colour into a
   pointer-events:none overlay layer that sits above the section background.
   Naive ancestor-walking reports the wrong backdrop, and so does
   elementsFromPoint (it skips pointer-events:none nodes). The ratios quoted
   below came from a geometric resolver that finds the smallest opaque box
   covering each text rect. Two earlier passes gave wrong answers before that
   was accounted for. Ratios are WCAG 2.1 contrast. */

:root{
  --se-ink:#0B0F19;        /* Obsidian Slate */
  --se-orange:#F79400;     /* Safety Orange  */
  --se-lime:#76B900;       /* Tech Lime      */
  --se-canvas:#F8FAFC;     /* Off-white base */
  --se-card:#FFFFFF;
  --se-line:#E2E8F0;
  --se-body:#334155;       /* 9.6:1 on canvas */
}

/* ─── 1. NAVIGATION ────────────────────────────────────────────────────────
   MEASURED: nav links rendered #000000 on a #0B0F19 header = 1.10:1.
   The main menu was effectively invisible: Who We Are, Services, Knowledge,
   Shop Online, Contact, Support, Ts & Cs. This is the worst defect on the
   site and the reason the header inverts to a light backdrop. */
.site-header,
.site-header .site-header-wrapper,
.kl-top-header,
.site-header .container-header{
  background-color:var(--se-canvas) !important;
  border-bottom:1px solid var(--se-line) !important;
}
.site-header a,
.site-header .main-nav > li > a,
.site-header .main-nav > li > a > span,
.kl-top-header a,
.site-header .zn-res-menuwrapper a{
  color:var(--se-ink) !important;    /* 17.4:1 on canvas */
  font-weight:700 !important;
}
.site-header a:hover,
.site-header a:focus,
.site-header .main-nav > li > a:hover,
.site-header .main-nav > li > a:hover > span,
.kl-top-header a:hover{
  color:var(--se-orange) !important; /* hover state only, never a resting
                                        state: orange on canvas is 2.3:1 */
}
/* active item shipped white-on-orange = 2.28:1; ink on orange is 8.6:1 */
.site-header .main-nav > li.current-menu-item > a,
.site-header .main-nav > li.current-menu-item > a > span,
.site-header .main-nav > li.active > a,
.site-header .main-nav > li.active > a > span{
  color:var(--se-ink) !important;
  background-color:var(--se-orange) !important;
}
/* dropdowns inherit the old dark scheme - re-light them */
.site-header .main-nav ul.sub-menu,
.site-header .main-nav li ul{
  background-color:var(--se-card) !important;
  border:1px solid var(--se-line) !important;
}
.site-header .main-nav ul.sub-menu a,
.site-header .main-nav ul.sub-menu a > span{
  color:var(--se-ink) !important;
  font-weight:600 !important;
}
.site-header .main-nav ul.sub-menu a:hover,
.site-header .main-nav ul.sub-menu a:hover > span{
  color:var(--se-orange) !important;
  background-color:var(--se-canvas) !important;
}
.site-header .logo-container,
.site-header .site-logo{background:transparent !important;}

/* ─── 2. HERO FLUSH BENEATH THE HEADER ─────────────────────────────────────
   MEASURED: header was position:absolute over the hero (hero top=0, header
   bottom=329) so the two overlapped instead of stacking. Returning the header
   to normal flow puts the hero flush beneath it with zero gap. */
.site-header{position:relative !important;}
.site-header--absolute,
.site-header--absolute-xs{position:relative !important;}
/* The hero reserved top padding to clear the absolute header. With the header
   in flow, that reserved space becomes exactly the blank gap this audit was
   asked to remove. */
.sehx__slides{padding-top:24px !important;}
.sehx{margin-top:0 !important;}

/* ─── 3. VERTICAL RHYTHM ───────────────────────────────────────────────────
   MEASURED: section padding of 120/80px and 100/100px on a 3,957px homepage.
   Tightened to the 30-40px threshold. The hero is excluded - it is a
   deliberate full-bleed, not an over-padded content section. */
section.zn_section:not(.sehx){
  padding-top:36px !important;
  padding-bottom:36px !important;
}
section.zn_section:not(.sehx) > .zn_section_size{
  padding-top:0 !important;
  padding-bottom:0 !important;
}
.media-container.eluid567fdb28{min-height:0 !important;}
.media-container-pb{padding-top:0 !important;padding-bottom:0 !important;}

/* ─── 4. BASE CANVAS ───────────────────────────────────────────────────────
   MEASURED: the mid-page band was a flat #F79400 slab 847px tall carrying
   white text at 2.28:1. Its background-image resolved to `none`, so the
   orange overlay meant to tint a photograph was tinting nothing. That slab is
   the large blank stretch in the brief, and the reason its text failed. */
body,
#page,
.page-wrapper,
section.zn_section:not(.sehx){
  background-color:var(--se-canvas) !important;
}
.zn-bgSource-overlay{background-color:var(--se-canvas) !important;}
.media-container .kl-title-block *,
.media-container .dn-heading,
.media-container .tbk__title{
  color:var(--se-ink) !important;    /* was #FFFFFF on orange = 2.28:1 */
}
.media-container .tbk__subtitle{color:var(--se-body) !important;} /* 9.6:1 */

/* pre-footer CTA band shipped #939393 on #D8D8D8 = 2.16:1 */
section.zn_section.eluid249c7445{background-color:var(--se-canvas) !important;}
section.zn_section.eluid249c7445 .tbk__subtitle,
section.zn_section.eluid249c7445 p{color:var(--se-body) !important;}
section.zn_section.eluid249c7445 .tbk__title,
section.zn_section.eluid249c7445 h1,
section.zn_section.eluid249c7445 h2{color:var(--se-ink) !important;}

/* ─── 5. CONTENT CARDS ─────────────────────────────────────────────────────
   White surface, 1px hairline, 3px Safety Orange top accent. */
.kl-iconbox,
ul.products li.product,
.testimonial-item{
  background-color:var(--se-card) !important;
  border:1px solid var(--se-line) !important;
  border-top:3px solid var(--se-orange) !important;
  border-radius:4px !important;
  padding:20px !important;
}
.kl-iconbox__title,
ul.products li.product .woocommerce-loop-product__title{
  color:var(--se-ink) !important;
  font-weight:700 !important;
}
ul.products li.product .price{
  color:var(--se-ink) !important;
  font-weight:700 !important;
}

/* ─── 6. CTA AND STATUS COLOURS ────────────────────────────────────────────
   Orange is a FILL, never a text colour on light: #F79400 on white is 2.3:1
   and would fail as body text. Ink on orange is 8.6:1. */
.btn-primary,
.zn_button--primary,
button.single_add_to_cart_button,
.woocommerce a.button.alt,
.woocommerce button.button.alt{
  background-color:var(--se-orange) !important;
  border-color:var(--se-orange) !important;
  color:var(--se-ink) !important;
  font-weight:700 !important;
}
.btn-primary:hover,
button.single_add_to_cart_button:hover,
.woocommerce a.button.alt:hover,
.woocommerce button.button.alt:hover{
  background-color:var(--se-lime) !important;
  border-color:var(--se-lime) !important;
  color:var(--se-ink) !important;    /* 7.4:1 on lime */
}
/* Raw lime as TEXT on white is 2.9:1 and fails; this darkened lime is 5.1:1
   and reads as the same brand colour. */
.in-stock,
.stock.in-stock,
.se-badge--live{
  color:#3D6100 !important;
  font-weight:700 !important;
}

/* ─── 7. KALLYAS DEMO FILLER ───────────────────────────────────────────────
   REVERSIBLE CONTENT DECISION - flagged in the deploy notes.
   "DEVELOPMENT / WEB DESIGN / STRATEGY" and the "Projects / Elements /
   Awards Won" counters are stock Kallyas demo copy. They describe a web
   design agency rather than an IT equipment and support business, and the
   counters are invented figures. Raising their contrast - the literal reading
   of the brief - would make fabricated statistics MORE prominent to
   customers. They are hidden instead. Delete this block to restore them. */
.zn_custom_container.eluid47972c0f{display:none !important;}

/* ─── 8. RESPONSIVE ────────────────────────────────────────────────────────
   The header is taller in flow than it was overlapping, so the mobile menu
   trigger and the hero need to stay clear of each other. */
@media (max-width:900px){
  section.zn_section:not(.sehx){
    padding-top:30px !important;
    padding-bottom:30px !important;
  }
  .sehx__slides{padding-top:16px !important;}
  .site-header .zn-res-menuwrapper a{color:var(--se-ink) !important;}
}

/* ─── 9. HEADER BACKDROP OVERRIDE ──────────────────────────────────────────
   MEASURED AFTER THE FIRST DEPLOY, and the reason this block exists:
   `.home #header.site-header` sets #0B0F19 with !important at specificity 120.
   The rule in section 1 above is `.site-header` at specificity 10, so the nav
   COLOUR change landed while the BACKGROUND change did not - leaving ink text
   on an ink header at 1.0:1, marginally worse than the 1.1:1 that shipped.
   Fixing half a contrast pair is worse than fixing neither.

   These selectors clear 120 (2 elements + 2 classes + 1 id = 122).

   Checked before inverting, not after: the logo artwork is dark - average
   luminance 110 over its opaque pixels, 59% transparent - so it stays legible
   on a light backdrop. A white-on-transparent logo would have made this fix
   destructive, and keeping the original logo intact was an explicit
   instruction. */
html body #header.site-header,
html body.home #header.site-header,
html body.home #header.site-header .site-header-wrapper,
html body #header.site-header.header--is-sticked{
  background-color:var(--se-canvas) !important;
  background-image:none !important;
}
html body #header.site-header .kl-top-header{
  background-color:var(--se-canvas) !important;
}
/* the theme marks this header nav-th--light / sheader-sh--light, meaning it
   expects pale text. On a light backdrop that inverts, so pin the nav to ink
   at a specificity that survives those theme classes. */
html body #header.site-header .main-nav > li > a,
html body #header.site-header .main-nav > li > a > span,
html body #header.site-header .kl-top-header a{
  color:var(--se-ink) !important;
  font-weight:700 !important;
}
html body #header.site-header .main-nav > li > a:hover,
html body #header.site-header .main-nav > li > a:hover > span{
  color:var(--se-orange) !important;
}

/* ─── 10. REGRESSIONS CAUSED BY SECTION 4 ──────────────────────────────────
   Section 4 set every zn_section to the off-white canvas. Two of them carry
   WHITE text that the theme had paired with a dark surface, so inverting the
   surface without inverting the text left white-on-off-white at 1.05:1 -
   invisible. Measured after deploy, not predicted.

   This is the same failure mode as the header: changing one half of a contrast
   pair is worse than changing neither. Both are repaired by restoring the dark
   surface rather than recolouring the text, because a dark footer is a normal
   enterprise pattern and the white text there is already AAA against it. */
html body section.zn_section#eluidc2d05bb4,
html body section.zn_section.eluidc2d05bb4{
  background-color:var(--se-ink) !important;   /* white on ink = 18.3:1 */
}
html body section.zn_section#eluidc2d05bb4 .kl-iconbox,
html body section.zn_section.eluidc2d05bb4 .kl-iconbox{
  background-color:transparent !important;      /* section 5 would otherwise
                                                   paint white cards here */
  border:0 !important;
  border-top:0 !important;
  padding:0 !important;
}
html body section.zn_section#eluidc2d05bb4 .kl-iconbox__title{
  color:#FFFFFF !important;
}

/* header top bar: white text, now on a light header */
html body #header.site-header .kl-header-toptext,
html body #header.site-header .kl-header-toptext *,
html body #header.site-header .site-header-top-wrapper a,
html body #header.site-header .site-header-top-wrapper span{
  color:var(--se-ink) !important;               /* 17.4:1 on canvas */
}

/* ─── 11. MOBILE MENU ──────────────────────────────────────────────────────
   MEASURED on a 375px viewport.

   (1) The off-canvas menu links ship as rgba(0,0,0,.45) on a white panel.
   Composited against white that is roughly #8C8C8C = 3.5:1, which fails AA for
   body text. Worth noting how this hid: a contrast check that reads the raw
   colour channels and ignores the alpha reports 21:1 here, because it sees
   pure black. The alpha has to be composited against the backdrop FIRST.
   The rules in section 1 did not reach these links - they target
   .zn-res-menuwrapper, and the live panel is ul.zn-res-menu-nav.

   (2) The burger tap target measures 20x22 CSS px. WCAG 2.2 SC 2.5.8 (AA)
   wants at least 24x24, and 44x44 is the comfortable thumb target. Growing the
   hit area does not change the icon itself. */
html body .zn-res-menu-nav a,
html body .zn-res-menu-nav a span,
html body ul.zn-res-menu-nav li > a{
  color:var(--se-ink) !important;      /* 18.3:1 on the white panel */
  opacity:1 !important;
}
html body ul.zn-res-menu-nav{
  background-color:var(--se-card) !important;
}
html body .zn-menuBurger,
html body .headernav-trigger{
  min-width:44px !important;
  min-height:44px !important;
  display:inline-flex !important;
  align-items:center !important;
  justify-content:center !important;
}

/* ─── 12. GLOSS / TRANSLUCENT HEADER ───────────────────────────────────────
   Requested: a glossy header at ~30% transparency.

   THE TRADE-OFF, resolved rather than ignored: section 2 put the header back
   into normal flow so the hero sits flush beneath it with zero gap. In flow,
   nothing passes behind the header, so translucency alone would show only the
   page canvas and read as no effect whatsoever. The pattern that actually
   delivers glass is the standard one: opaque and in flow at the top of the
   page, translucent and floating once the user scrolls. Kallyas already adds
   .header--is-sticked on scroll, so that state is what carries the effect.

   CONTRAST, CHECKED AT BOTH EXTREMES rather than assumed - a translucent bar
   means the text sits on whatever scrolls beneath it:
     70% canvas over the dark hero (#0B0F19) composites to ~rgb(177,178,180),
       and ink text on that is 9.0:1  - passes AA
     70% canvas over pure white composites to ~rgb(250,250,251),
       and ink text on that is 17.0:1 - passes AAA
   So the nav stays legible over the darkest and lightest things on the site.
   Anything below about 60% opacity fails the dark case, which is why this
   stops at 70%. */
html body #header.site-header,
html body.home #header.site-header{
  background-color:rgba(248,250,252,.72) !important;
  background-image:linear-gradient(180deg,rgba(255,255,255,.55),rgba(255,255,255,.12)) !important;
  -webkit-backdrop-filter:saturate(1.55) blur(14px);
          backdrop-filter:saturate(1.55) blur(14px);
  box-shadow:inset 0 -1px 0 rgba(11,15,25,.10), 0 1px 14px rgba(11,15,25,.05);
  border-bottom:0 !important;
}
/* floating state: this is where the translucency is actually visible */
html body #header.site-header.header--is-sticked{
  position:fixed !important;
  top:0 !important; left:0 !important; right:0 !important;
  z-index:9999 !important;
  background-color:rgba(248,250,252,.78) !important;
  box-shadow:inset 0 -1px 0 rgba(11,15,25,.10), 0 6px 24px rgba(11,15,25,.10);
}
/* Without backdrop-filter there is no blur to separate text from whatever is
   behind it, so fall back to a nearly opaque bar rather than a smeared one. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  html body #header.site-header,
  html body #header.site-header.header--is-sticked{
    background-color:rgba(248,250,252,.96) !important;
  }
}

/* ─── 13. CANVAS TEXTURE ───────────────────────────────────────────────────
   The page reads as flat white once the orange slab is neutralised, because
   that band's background-image resolves to `none` - the overlay was tinting a
   photograph that is not there. Until that image is restored, this gives the
   canvas the same blueprint-grid motif the hero already uses, so the light
   sections have depth instead of being blank.

   KEPT DELIBERATELY FAINT so it cannot cost contrast. The darkest pixel of the
   grid is rgba(11,15,25,.035) over #F8FAFC, which composites to ~rgb(240,242,243):
     ink   #0B0F19 on that = 16.9:1
     body  #334155 on that =  9.2:1
   Both still clear AA with room to spare. Cards stay solid white, so card text
   never sits on the pattern at all. */
html body{
  background-color:var(--se-canvas) !important;
  background-image:
    linear-gradient(rgba(11,15,25,.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(11,15,25,.035) 1px, transparent 1px),
    radial-gradient(62% 44% at 88% 2%,  rgba(247,148,0,.075), transparent 62%),
    radial-gradient(54% 42% at 2% 98%,  rgba(118,185,0,.065), transparent 60%) !important;
  background-size:64px 64px, 64px 64px, auto, auto !important;
  background-attachment:fixed !important;
  background-repeat:repeat, repeat, no-repeat, no-repeat !important;
}
/* sections go transparent so the canvas shows through them; without this the
   texture would be painted over by the flat fill from section 4 */
html body section.zn_section:not(.sehx),
html body #page,
html body .page-wrapper,
html body .sesvc,
html body .zn-bgSource-overlay{
  background-color:transparent !important;
}
/* the footer is the one section that must stay opaque and dark */
html body section.zn_section#eluidc2d05bb4,
html body section.zn_section.eluidc2d05bb4{
  background-color:var(--se-ink) !important;
}

/* ─── 14. ALPHA-MASKED FAILURES ────────────────────────────────────────────
   These six were failing the whole time and every earlier pass reported them
   as fine. The reason is worth writing down: the theme sets these colours as
   rgba black with an alpha (roughly .40-.45). A contrast checker that reads the
   raw channels sees PURE BLACK and reports ~21:1. The alpha has to be
   composited against the backdrop before the ratio means anything - composited,
   these land between 2.71:1 and 3.35:1 and fail AA.

   I had already identified this exact bug in the mobile menu (section 11) and
   fixed it there without fixing the audit method that hid it, so the "zero
   failures" result that followed was measured with the flaw still in place.

   MEASURED, composited:
     section eyebrows  #8C8C8C on white = 3.35:1  -> ink       17.4:1
     body copy         #999999 on white = 2.85:1  -> --se-body  9.6:1
     block headings    #9D9D9D on white = 2.71:1  -> ink       17.4:1
   Scoped to exclude the dark footer, where pale text is correct and ink would
   recreate the exact fault this fixes. */
html body section.zn_section:not(#eluidc2d05bb4) .tbk__title,
html body section.zn_section:not(#eluidc2d05bb4) .dn-heading,
html body section.zn_section:not(#eluidc2d05bb4) h2,
html body section.zn_section:not(#eluidc2d05bb4) h3{
  color:var(--se-ink) !important;
  opacity:1 !important;
}
html body section.zn_section:not(#eluidc2d05bb4) .tbk__subtitle,
html body section.zn_section:not(#eluidc2d05bb4) .kl-iconbox__desc,
html body section.zn_section:not(#eluidc2d05bb4) .kl-title-block p,
html body section.zn_section:not(#eluidc2d05bb4) .testimonial-text{
  color:var(--se-body) !important;
  opacity:1 !important;
}

/* ─── 15. HERO TITLE — REGRESSION FIX (my own rule broke it) ────────────────
   Section 14 added `html body section.zn_section:not(#eluidc2d05bb4) h2` to
   force dark headings on light sections. The hero's headline is ALSO an <h2>
   inside a zn_section, so it inherited ink — on an ink background. The banner
   headline was rendering at 1:1 contrast, i.e. completely invisible, and it
   took a screenshot from Steve to catch it.

   The lesson is the same one that keeps recurring here: a broad selector
   written for one section will find every other element that happens to match.
   Specificity of the offending rule is 113 (:not(#id) counts as an id), so this
   override is built to clear it at 133 rather than piling on !important. */
html body section.zn_section:not(#eluidc2d05bb4) .sehx .sehx__title{
  color:#FFFFFF !important;              /* 18.3:1 on the ink banner */
}
html body section.zn_section:not(#eluidc2d05bb4) .sehx .sehx__title em{
  color:#F07E13 !important;              /* 6.6:1 on ink - the accent line */
}

/* ─── 16. TOP BAR IN BRAND ORANGE ──────────────────────────────────────────
   Requested: the grey "CALL US NOW" strip becomes Safety Orange.
   Text goes to ink, NOT white: white on #F79400 is 2.28:1 and fails AA, which
   is the same mistake the original theme made on its active nav item. Ink on
   orange is 8.6:1. */
html body #header.site-header .kl-top-header,
html body #header.site-header .site-header-top-wrapper,
html body #header.site-header .site-header-top-wrapper .container{
  background-color:var(--se-orange) !important;
  background-image:none !important;
}
html body #header.site-header .site-header-top-wrapper,
html body #header.site-header .site-header-top-wrapper *,
html body #header.site-header .kl-header-toptext,
html body #header.site-header .kl-header-toptext *,
html body #header.site-header .site-header-top-wrapper a{
  color:var(--se-ink) !important;        /* 8.6:1 on Safety Orange */
}
html body #header.site-header .site-header-top-wrapper a:hover{
  color:#FFFFFF !important;              /* hover only: 2.28:1, never a resting state */
}

/* ─── 17. HERO BANNER: GLOSS + TRANSLUCENCY ────────────────────────────────
   Requested: glossy, 40% transparent.

   THE PROBLEM, MEASURED BEFORE BUILDING IT: the banner is #0B0F19 sitting on
   an off-white page. At 60% opacity it composites to rgb(106,109,116) — a mid
   grey — and every piece of text on it collapses:
       eyebrow  10.54:1 -> 2.85   FAIL
       counter   7.77:1 -> 2.10   FAIL
       subtitle 12.29:1 -> 3.33   FAIL
       tabs      7.77:1 -> 2.10   FAIL
   Literal 40% transparency over the page would make the banner unreadable.

   THE RESOLUTION: keep the translucency, change what sits BEHIND it. A dark
   gradient underlay means the 60% layer reveals depth and movement rather than
   the white page, so the glass reads as glass and the text keeps its contrast.
   Plus a real gloss sheen and a backdrop blur. */
html body .zn_custom_html.eluidd8417bf5,
html body section.zn_section.eluid5a251cc3{
  background:radial-gradient(120% 110% at 78% 8%, #17223D 0%, #0B0F19 62%) !important;
}
html body .sehx{
  background-color:rgba(11,15,25,.60) !important;
  -webkit-backdrop-filter:blur(14px) saturate(1.35);
          backdrop-filter:blur(14px) saturate(1.35);
  position:relative;
}
/* the gloss itself: a highlight falling off down the banner. z-index 1 keeps it
   UNDER the content (.sehx__stage is z-index 2, .sehx__bar is 3). */
html body .sehx::after{
  content:"";
  position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(180deg,
    rgba(255,255,255,.12) 0%,
    rgba(255,255,255,.04) 32%,
    rgba(255,255,255,0)   68%);
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  html body .sehx{background-color:rgba(11,15,25,.82) !important;}
}

/* ─── 18. HEADER CORRECTION — the class names are misleading ────────────────
   Section 16 turned the WHOLE header orange, including the nav row, and made
   the logo and menu unreadable. Cause, measured on the live DOM:

     .site-header-top-wrapper   54px   = the thin "CALL US NOW" strip
     .kl-top-header            108px   = THE MAIN NAV ROW

   I read ".kl-top-header" as "the header's top bar" and targeted it for the
   orange. It is the menu. The name says top, the element is the nav.
   **Verify what a class actually IS on the page before styling it by name.**

   Correct split, per Steve: orange strip, light readable menu. */

/* the thin strip: orange, ink text (8.6:1) */
html body #header.site-header .site-header-top-wrapper,
html body #header.site-header .site-header-top-wrapper .siteheader-container,
html body #header.site-header .site-header-top-wrapper .container{
  background-color:var(--se-orange) !important;
  background-image:none !important;
}

/* the NAV ROW: back to the light glass, at 90% so the menu reads clearly */
html body #header.site-header .kl-top-header,
html body #header.site-header .kl-top-header .container,
html body #header.site-header .site-header-main-wrapper{
  background-color:rgba(248,250,252,.90) !important;
  background-image:none !important;
}
html body #header.site-header,
html body.home #header.site-header{
  background-color:rgba(248,250,252,.90) !important;
}
html body #header.site-header.header--is-sticked{
  background-color:rgba(248,250,252,.93) !important;
}
/* nav links stay ink on the light row (17.4:1); the strip keeps ink on orange */
html body #header.site-header .kl-top-header a,
html body #header.site-header .kl-top-header a > span,
html body #header.site-header .main-nav > li > a,
html body #header.site-header .main-nav > li > a > span{
  color:var(--se-ink) !important;
}
html body #header.site-header .kl-top-header a:hover,
html body #header.site-header .main-nav > li > a:hover,
html body #header.site-header .main-nav > li > a:hover > span{
  color:var(--se-orange) !important;
}
/* the logo sits on the light row again, so no filter or backdrop of its own */
html body #header.site-header .logo-container,
html body #header.site-header .site-logo,
html body #header.site-header .site-logo-anch{
  background:transparent !important;
  filter:none !important;
}

/* ─── 19. DARK UNDERLAY LEAKED INTO THE SERVICES SECTION — SCOPE FIX ────────
   Section 17 put a dark radial gradient on `.zn_custom_html.eluidd8417bf5` to
   sit behind the translucent hero. **That container also holds the services
   grid**, because the grid was appended to the same markup file as the hero.
   So the services section went dark while its text stayed ink — dark on dark,
   unreadable. Steve caught it in a screenshot.

   THE PATTERN, THIRD TIME TODAY: I styled a CONTAINER to affect ONE child.
   Section 4 did it to the footer, section 14 to the hero headline, this one to
   the services grid. **Style the element, not its parent, unless every child
   should inherit the change.**

   Fix: the dark backdrop moves INSIDE the hero as its own layer, so it cannot
   reach a sibling. Everything outside the banner returns to white. */
html body .zn_custom_html.eluidd8417bf5,
html body section.zn_section.eluid5a251cc3{
  background:transparent !important;
}

/* the hero carries its own dark backdrop as a layer at z-index 0.
   .sehx__stage is z-index 2 and .sehx__bar is 3, so content stays above it. */
html body .sehx::before{
  content:"";
  position:absolute; inset:0; z-index:0; pointer-events:none;
  background:radial-gradient(120% 110% at 78% 8%, #17223D 0%, #0B0F19 62%);
}
html body .sehx{
  background-color:rgba(11,15,25,.60) !important;
  -webkit-backdrop-filter:blur(14px) saturate(1.35);
          backdrop-filter:blur(14px) saturate(1.35);
  position:relative;
  isolation:isolate;              /* keeps these layers out of the page stack */
}

/* everything below the banner stays on the white/off-white canvas */
html body .sesvc{
  background-color:var(--se-canvas) !important;
  background-image:none !important;
}
html body .sesvc__card{
  background-color:var(--se-card) !important;
}

/* ─── 20. ON-SALE GRID ─────────────────────────────────────────────────────
   Styling for the block that replaces the old "What we'll do" demo section.
   Same card language as the services grid: white surface, hairline, 3px orange
   top accent. Prices and titles in ink; the sale price in a DARKENED orange
   because #F79400 as text on white is 2.3:1 and fails. */
html body .sesale{background-color:var(--se-canvas) !important;padding:44px 0 52px;}
html body .sesale__wrap{max-width:1170px;margin:0 auto;padding:0 15px;}
html body .sesale__eyebrow{display:inline-flex;align-items:center;gap:9px;margin:0 0 12px;
  font-size:12px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:#3D6100;}
html body .sesale__eyebrow u{width:7px;height:7px;background:var(--se-lime);flex:none;
  text-decoration:none;transform:rotate(45deg);}
html body .sesale__h2{margin:0 0 8px;font-size:clamp(26px,3vw,38px);line-height:1.12;
  font-weight:700;letter-spacing:-.02em;color:var(--se-ink);}
html body .sesale__h2 em{font-style:normal;color:var(--se-ink);
  box-shadow:inset 0 -0.18em 0 rgba(247,148,0,.55);}
html body .sesale__lede{margin:0 0 26px;font-size:16.5px;line-height:1.6;color:var(--se-body);}
html body .sesale ul.products{display:grid !important;gap:20px !important;
  grid-template-columns:repeat(4,minmax(0,1fr)) !important;margin:0 !important;padding:0 !important;
  list-style:none !important;}
@media (max-width:1000px){html body .sesale ul.products{grid-template-columns:repeat(2,minmax(0,1fr)) !important;}}
@media (max-width:600px){html body .sesale ul.products{grid-template-columns:1fr !important;}}
html body .sesale ul.products li.product{
  background:var(--se-card) !important;border:1px solid var(--se-line) !important;
  border-top:3px solid var(--se-orange) !important;border-radius:4px !important;
  padding:16px !important;margin:0 !important;width:auto !important;float:none !important;}
html body .sesale ul.products li.product img{border-radius:3px;}
html body .sesale ul.products li.product .woocommerce-loop-product__title{
  color:var(--se-ink) !important;font-size:15px !important;font-weight:700 !important;
  line-height:1.35 !important;}
html body .sesale ul.products li.product .price{color:var(--se-ink) !important;font-weight:700 !important;}
html body .sesale ul.products li.product .price del{color:#64748B !important;font-weight:400 !important;}
html body .sesale ul.products li.product .price ins{color:#8A5200 !important;text-decoration:none !important;}
html body .sesale ul.products li.product .onsale{
  background:var(--se-orange) !important;color:var(--se-ink) !important;font-weight:700 !important;}
html body .sesale__foot{margin:26px 0 0;}
html body .sesale__btn{display:inline-flex;align-items:center;gap:9px;padding:12px 24px;
  font-size:14.5px;font-weight:700;text-decoration:none;background:var(--se-orange);
  color:var(--se-ink);
  clip-path:polygon(0 0,100% 0,100% calc(100% - 9px),calc(100% - 9px) 100%,0 100%);}
html body .sesale__btn:hover{background:var(--se-lime);color:var(--se-ink);}

/* ─── 21. SALE GRID: UNIFORM CARDS ─────────────────────────────────────────
   MEASURED on the live grid: card heights came out 216, 397, 437, 484, 484,
   496, 554, 554 px — a ragged, broken-looking row, which is what Steve circled.
   Cause: the image box grows with each product photo's own aspect ratio, and
   the title wraps to a different number of lines per product.

   Fix is structural, not cosmetic: a FIXED image box with object-fit:contain
   (so nothing is cropped), a title clamped to a fixed number of lines, and the
   price pinned to the bottom of the card so every price sits on the same line
   across the row. */
html body .sesale ul.products li.product{
  display:flex !important;
  flex-direction:column !important;
  height:100% !important;
}
html body .sesale ul.products{align-items:stretch !important;}

/* fixed-height image box; contain, never crop */
html body .sesale li.product .kw-prodimage,
html body .sesale li.product .woocommerce-LoopProduct-link,
html body .sesale li.product .zn_badge_container{
  display:block !important;
}
html body .sesale li.product img{
  width:100% !important;
  height:180px !important;
  object-fit:contain !important;
  background:#fff !important;
  margin:0 0 12px !important;
}

/* titles: same height on every card, clamped to 3 lines */
html body .sesale li.product .kw-details-title,
html body .sesale li.product .woocommerce-loop-product__title{
  color:var(--se-ink) !important;
  font-size:14.5px !important;
  font-weight:700 !important;
  line-height:1.35 !important;
  margin:0 0 6px !important;
  min-height:calc(1.35em * 3);
  display:-webkit-box;
  -webkit-line-clamp:3;
  -webkit-box-orient:vertical;
  overflow:hidden;
}

/* the theme's hover blurb is alpha-0 by design; keep it out of the flow so it
   cannot add invisible height and re-introduce the ragged rows */
html body .sesale li.product .kw-details-desc,
html body .sesale li.product .product-list-item > p{
  display:none !important;
}

/* price pinned to the bottom so it lines up across the row */
html body .sesale li.product .price{
  margin-top:auto !important;
  padding-top:8px !important;
  font-size:15px !important;
}
html body .sesale li.product .price del{display:inline-block;margin-right:6px;}

/* actions sit under the price, consistent height */
html body .sesale li.product .actions-addtocart,
html body .sesale li.product .actions-moreinfo{
  color:var(--se-ink) !important;
  font-weight:700 !important;
  font-size:13px !important;
}

/* ─── 22. SALE GRID: IMAGES WERE INVISIBLE (opacity 0) ──────────────────────
   MEASURED: every product image in the grid was fully loaded — natural size
   270x270, complete=true, rendered 338x180 — and sitting at **opacity 0**.
   Kallyas fades product images in by adding a class on scroll/hover; inside a
   shortcode-rendered grid that hook never fires, so the images stayed at zero.
   They were never missing; they were transparent. This is why the cards looked
   like empty grey boxes.

   Forced visible, with the fade-in transition removed so nothing can leave them
   at zero again. */
html body .sesale li.product img,
html body .sesale li.product .kw-prodimage-img,
html body .sesale li.product .kw-prodimage-img-secondary{
  opacity:1 !important;
  visibility:visible !important;
  transition:none !important;
  animation:none !important;
  transform:none !important;
}
/* the theme swaps to a secondary image on hover; with both forced visible they
   would stack, so keep only the primary in flow */
html body .sesale li.product .kw-prodimage-img-secondary{display:none !important;}

/* ─── 23. SALE GRID: FULL PAGE WIDTH ───────────────────────────────────────
   The grid sits inside `.zn_pb_wrapper`, the page-builder content wrapper, so
   it inherits that column's width rather than the page's. On desktop that left
   the products in one narrow column with a large empty area beside them — the
   gap Steve circled. Give the section its own container width instead of
   relying on whatever it happens to be nested in. */
html body .sesale{width:100% !important;}
html body .sesale__wrap{
  max-width:1170px !important;
  width:100% !important;
  margin:0 auto !important;
  padding:0 15px !important;
  box-sizing:border-box !important;
}
html body .sesale .woocommerce{width:100% !important;}
html body .sesale ul.products{
  display:grid !important;
  grid-template-columns:repeat(4,minmax(0,1fr)) !important;
  width:100% !important;
}
@media (max-width:1000px){
  html body .sesale ul.products{grid-template-columns:repeat(2,minmax(0,1fr)) !important;}
}
@media (max-width:600px){
  html body .sesale ul.products{grid-template-columns:1fr !important;}
}

/* ─── 24. CAMPAIGN PANEL IN THE SERVICES GRID ──────────────────────────────
   The "Build" group holds 4 cards in a 3-column grid, so the fourth sat alone
   and left two empty cells. This panel fills them with a conversion prompt
   rather than filler.

   Deliberately NOT an offer. It invites a conversation; it does not promise a
   free assessment, a discount, or a turnaround time. Any actual promotional
   claim has to come from Steve - inventing one would put a commitment on a live
   business site that nobody agreed to honour. */
html body .sesvc__promo{
  grid-column:span 2;
  background:linear-gradient(135deg,#131C2E 0%,#0B0F19 70%) !important;
  border:1px solid #223052 !important;
  border-top:3px solid var(--se-orange) !important;
  justify-content:center;
}
@media (max-width:1000px){html body .sesvc__promo{grid-column:span 2;}}
@media (max-width:660px){html body .sesvc__promo{grid-column:span 1;}}
html body .sesvc__promo:hover{transform:none;box-shadow:none;}
html body .sesvc__promo-eyebrow{
  margin:0 0 10px;font-size:12px;font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;color:#A3CF3F;      /* 10.5:1 on the dark panel */
}
html body .sesvc__promo-title{
  margin:0 0 12px;font-size:clamp(20px,2.1vw,26px);line-height:1.2;
  font-weight:700;letter-spacing:-.01em;color:#FFFFFF;   /* 18.4:1 */
}
html body .sesvc__promo-body{
  margin:0 0 20px;font-size:15px;line-height:1.62;color:#C6D0DE;  /* 12.3:1 */
  max-width:52ch;
}
html body .sesvc__promo-cta{display:flex;flex-wrap:wrap;gap:12px;margin:0;}
html body .sesvc__promo-btn{
  display:inline-flex;align-items:center;gap:9px;padding:12px 24px;
  font-size:14.5px;font-weight:700;text-decoration:none;border:1px solid transparent;
  clip-path:polygon(0 0,100% 0,100% calc(100% - 9px),calc(100% - 9px) 100%,0 100%);
  transition:background .18s ease,color .18s ease,transform .18s ease;
}
html body .sesvc__promo-btn--go{background:var(--se-orange);color:var(--se-ink);}  /* 8.6:1 */
html body .sesvc__promo-btn--go:hover{background:#FF8F22;transform:translateY(-2px);}
html body .sesvc__promo-btn--ghost{background:transparent;color:#E7EBF2;border-color:rgba(255,255,255,.26);}
html body .sesvc__promo-btn--ghost:hover{border-color:#A3CF3F;color:#A3CF3F;}
@media (prefers-reduced-motion:reduce){
  html body .sesvc__promo-btn{transition:none}
  html body .sesvc__promo-btn--go:hover{transform:none}
}

/* ─── 25. CLEARFIX PSEUDO-ELEMENTS WERE EATING GRID CELLS ───────────────────
   MEASURED: `.sesale ul.products::before` and `::after` each computed to
   display:table, 270px x 356px — the exact size of a product card.

   The theme uses the classic float clearfix (`ul.products::before/::after {
   content:""; display:table }`). That is harmless while the list is floated.
   The moment the list becomes `display:grid`, THOSE PSEUDO-ELEMENTS BECOME
   GRID ITEMS and each consumes a cell: one at the start of row 1 and one at
   the end. Twelve products then laid out 3 / 4 / 4 / 1 across four rows with a
   hole at each end, instead of a clean 3 x 4.

   This is a general trap, not a one-off: any floated list converted to grid or
   flex inherits phantom items from its clearfix. Kill them explicitly. */
html body .sesale ul.products::before,
html body .sesale ul.products::after,
html body .sesale .woocommerce::before,
html body .sesale .woocommerce::after{
  display:none !important;
  content:none !important;
}

/* ─── 26. "WE DO THINGS DIFFERENTLY" — COLLAPSE THE DEAD SPACE ──────────────
   That band was originally an 847px full-bleed with a background photo behind
   it. The photo resolves to `none` (section 4), and the Kallyas demo tiles that
   filled its lower half are hidden (section 7). What is left is the heading, a
   genuine paragraph of StepEdge vision copy, and a large empty area where the
   demo used to be.

   RESTRUCTURED RATHER THAN FILLED. The honest options were to collapse it or to
   invent content, and inventing capability claims for a live business site is
   not mine to do. The copy that IS real is kept and given a tighter two-column
   band; everything the hidden demo was holding up is removed from the flow.

   If Steve wants this space used, the material has to come from him -
   testimonials, certifications, partner logos, or case studies. Any of those
   would drop straight into this container. */
html body .media-container.eluid567fdb28,
html body .media-container.eluid567fdb28 .media-container-pb,
html body .media-container.eluid567fdb28 .zn-bgSource,
html body .media-container.eluid567fdb28 .row{
  min-height:0 !important;
  height:auto !important;
}
html body .media-container.eluid567fdb28 .znColumnElement-innerWrapper,
html body .media-container.eluid567fdb28 .znColumnElement-innerContent,
html body .media-container.eluid567fdb28 [class*="col-md"]{
  min-height:0 !important;
  height:auto !important;
  padding-bottom:0 !important;
}
/* the section itself: a compact band, not a full-height hero */
html body section.zn_section.eluid7e9cd6e5,
html body .media-container.eluid567fdb28{
  padding-top:34px !important;
  padding-bottom:34px !important;
}
/* heading and copy sit level, so the band reads as one row rather than two
   columns of very different height */
html body .media-container.eluid567fdb28 .row{
  display:flex !important;
  flex-wrap:wrap !important;
  align-items:center !important;
  gap:8px 0 !important;
}
html body .media-container.eluid567fdb28 .dn-heading{
  margin-bottom:6px !important;
}
html body .media-container.eluid567fdb28 .tbk__subtitle{
  margin-bottom:0 !important;
}
/* nothing the hidden demo block leaves behind should hold height open */
html body .zn_custom_container.eluid47972c0f{
  display:none !important;
  height:0 !important;
  min-height:0 !important;
  margin:0 !important;
  padding:0 !important;
}

/* ─── 27. THE REMAINING GAP WAS MARGIN, NOT EMPTY CONTENT ───────────────────
   MEASURED the 504px band precisely:
       34 + 34   section padding
       10 + 10   .media-container-pb margins
       50 + 30   .kl-title-block margins   <- the real culprit
          336    the actual paragraph
   So 168px of a 504px band was whitespace the theme adds around a title block
   that used to sit on a full-bleed background photo. With the photo gone and
   the demo tiles hidden, that breathing room reads as a hole.

   Trimmed to what the content needs. The left column's heading is also pinned
   to the top rather than vertically centred, so it lines up with the paragraph
   beside it instead of floating in the middle of its own column. */
html body .media-container.eluid567fdb28 .kl-title-block{
  margin-top:0 !important;
  margin-bottom:0 !important;
}
html body .media-container.eluid567fdb28 .media-container-pb{
  margin-top:0 !important;
  margin-bottom:0 !important;
}
html body .media-container.eluid567fdb28 h2.dn-heading{
  margin-top:0 !important;
  margin-bottom:4px !important;
}
html body .media-container.eluid567fdb28 .row{
  align-items:flex-start !important;   /* was centre - floated the heading */
}
html body section.zn_section.eluid7e9cd6e5,
html body .media-container.eluid567fdb28{
  padding-top:28px !important;
  padding-bottom:28px !important;
}

/* ─── 28. BRANDED PLACEHOLDER FOR IMAGELESS PRODUCTS ───────────────────────
   MEASURED on /shop/: 13 of 41 product blocks contain NO <img> tag at all -
   Kallyas renders an EMPTY .kw-prodimage div for a product without a photo,
   bypassing WooCommerce's placeholder system entirely. That is why the
   woocommerce_placeholder_img_src filter (also deployed) never fires on these,
   and why customers saw blank boxes.

   Only 57 of 2,517 imageless products have a photo anywhere in the full
   supplier feeds (55,077 images checked, 2026-08-27); those 57 are being
   attached for real. The rest get this tile: a clearly-labelled "IMAGE TO
   FOLLOW" placeholder, not fake product imagery.

   :has() targets containers with no img; the plain rule beneath it paints the
   tile BEHIND real images too as a load-state, which object-fit:contain photos
   on white cover completely. */
html body ul.products li.product .kw-prodimage:not(:has(img)),
html body .sesale li.product .kw-prodimage:not(:has(img)){
  background:#fff url('/wp-content/mu-plugins/stepedge-placeholder.svg') center/70% no-repeat !important;
  min-height:220px !important;
  display:block !important;
}

/* ─── 29. SHOP BANNER: MATCH THE PRODUCT-PAGE SIZE ─────────────────────────
   MEASURED: the shop archive's .page-subheader/.bgback renders 361px tall;
   a product page's renders 108px. Steve wants the shop to match the product
   page. Scoped to WooCommerce archive body classes so no other page's banner
   changes. */
html body.woocommerce-shop .page-subheader,
html body.post-type-archive-product .page-subheader,
html body.tax-product_cat .page-subheader,
html body.woocommerce-shop .page-subheader .bgback,
html body.post-type-archive-product .page-subheader .bgback,
html body.tax-product_cat .page-subheader .bgback{
  height:108px !important;
  min-height:108px !important;
  max-height:108px !important;
}
html body.woocommerce-shop .page-subheader .bgback{
  background-position:center 35% !important;   /* keep a sensible crop */
}
/* title scales down with the band */
html body.woocommerce-shop .page-subheader h1,
html body.woocommerce-shop .page-subheader h2,
html body.woocommerce-shop .page-subheader .subheader-maintitle,
html body.post-type-archive-product .page-subheader .subheader-maintitle{
  font-size:clamp(20px,2.2vw,28px) !important;
  margin:0 !important;
}
html body.woocommerce-shop .page-subheader .subheader-subtitle{
  display:none !important;   /* "Shop category here with product list" is
                                builder filler text, not copy */
}

/* ─── 30. CHECKOUT / CART / ACCOUNT BANNERS: SAME 108px AS THE REST ────────
   Rule 29 shrank the shop archive banner; Steve then circled the checkout
   banner still at 361px. Same treatment for the whole shop journey - a buyer
   at checkout needs the form, not a third of a screen of server-room photo. */
html body.woocommerce-checkout .page-subheader,
html body.woocommerce-cart .page-subheader,
html body.woocommerce-account .page-subheader,
html body.woocommerce-checkout .page-subheader .bgback,
html body.woocommerce-cart .page-subheader .bgback,
html body.woocommerce-account .page-subheader .bgback{
  height:108px !important;
  min-height:108px !important;
  max-height:108px !important;
}
html body.woocommerce-checkout .page-subheader h1,
html body.woocommerce-checkout .page-subheader .subheader-maintitle,
html body.woocommerce-cart .page-subheader .subheader-maintitle,
html body.woocommerce-account .page-subheader .subheader-maintitle{
  font-size:clamp(20px,2.2vw,28px) !important;
  margin:0 !important;
}

/* ─── 31. ALL PAGE BANNERS: 108px, GLOBALLY ────────────────────────────────
   Steve has now asked for the same reduction on shop, checkout and Ts & Cs.
   Three requests with an identical answer means the rule was scoped wrong -
   this replaces the per-page rules 29/30 with one site-wide rule. Every
   `.page-subheader` banner becomes 108px. The home/about/services hero is a
   different element (.sehx) and is untouched. Title contrast on busy photos
   is covered by the shadow below rather than hoping each image is dark. */
html body .page-subheader,
html body .page-subheader .bgback{
  height:108px !important;
  min-height:108px !important;
  max-height:108px !important;
}
html body .page-subheader h1,
html body .page-subheader h2,
html body .page-subheader .subheader-maintitle{
  font-size:clamp(20px,2.2vw,28px) !important;
  margin:0 !important;
  color:#FFFFFF !important;
  text-shadow:0 1px 8px rgba(11,15,25,.85), 0 0 2px rgba(11,15,25,.9);
}
html body .page-subheader .subheader-subtitle{display:none !important;}

/* ─── 32. BUILDER PHOTO BANNERS: SAME 108px, GENERICALLY ───────────────────
   Rule 31 shrank `.page-subheader` banners; the Ts & Cs banner turned out to
   be a THIRD pattern - a Zion builder section (#eluid28eb0312, 424px) with a
   `.zn-bgSource-image` background and a lone heading. Other builder pages
   will have the same shape under different ids, so this matches the SHAPE:
   the first zn_section on the page that carries a builder background image
   and does NOT contain the hero (.sehx). One rule ends the per-page chase.
   Browsers without :has() keep the tall banner - a graceful, not broken,
   fallback. */
html body #page_wrapper > section.zn_section:first-of-type:has(.zn-bgSource-image):not(:has(.sehx)),
html body .zn_page_wrapper > section.zn_section:first-of-type:has(.zn-bgSource-image):not(:has(.sehx)),
html body section.zn_section:first-of-type:has(> .zn-bgSource .zn-bgSource-image):not(:has(.sehx)){
  height:108px !important;
  min-height:108px !important;
  max-height:108px !important;
  display:flex !important;
  align-items:center !important;
  overflow:hidden !important;
  padding-top:0 !important;
  padding-bottom:0 !important;
}
html body section.zn_section:first-of-type:has(.zn-bgSource-image):not(:has(.sehx)) .zn_section_size,
html body section.zn_section:first-of-type:has(.zn-bgSource-image):not(:has(.sehx)) [class*="zn-section-height"],
html body section.zn_section:first-of-type:has(.zn-bgSource-image):not(:has(.sehx)) .znColumnElement-innerWrapper,
html body section.zn_section:first-of-type:has(.zn-bgSource-image):not(:has(.sehx)) .row{
  min-height:0 !important;
  height:auto !important;
  width:100%;
}
html body section.zn_section:first-of-type:has(.zn-bgSource-image):not(:has(.sehx)) h1,
html body section.zn_section:first-of-type:has(.zn-bgSource-image):not(:has(.sehx)) h2,
html body section.zn_section:first-of-type:has(.zn-bgSource-image):not(:has(.sehx)) h3{
  font-size:clamp(20px,2.2vw,28px) !important;
  margin:0 !important;
  color:#FFFFFF !important;
  text-shadow:0 1px 8px rgba(11,15,25,.85), 0 0 2px rgba(11,15,25,.9);
}
