/*
Theme Name: Hello Elementor Child
Theme URI: https://opalcaulking.com.au/
Description: Child theme for Opal Caulking Services. Holds self-hosted Poppins, design-token custom properties and build-specific CSS. Built by Get Shielded.
Author: Mohammad Emmon
Author URI: https://mohammademmon.com
Template: hello-elementor
Version: 1.73.0
Text Domain: hello-elementor-child
*/

/* ============================================================
   Poppins — self-hosted (no Google Fonts CDN)
   woff2 first, ttf fallback. font-display:swap per tokens.
   ============================================================ */
@font-face{
  font-family:'Poppins';font-style:normal;font-weight:300;font-display:swap;
  src:url('fonts/Poppins-Light.woff2') format('woff2'),
      url('fonts/Poppins-Light.ttf') format('truetype');
}
@font-face{
  font-family:'Poppins';font-style:normal;font-weight:400;font-display:swap;
  src:url('fonts/Poppins-Regular.woff2') format('woff2'),
      url('fonts/Poppins-Regular.ttf') format('truetype');
}
@font-face{
  font-family:'Poppins';font-style:normal;font-weight:500;font-display:swap;
  src:url('fonts/Poppins-Medium.woff2') format('woff2'),
      url('fonts/Poppins-Medium.ttf') format('truetype');
}
@font-face{
  font-family:'Poppins';font-style:normal;font-weight:700;font-display:swap;
  src:url('fonts/Poppins-Bold.woff2') format('woff2'),
      url('fonts/Poppins-Bold.ttf') format('truetype');
}

/* ============================================================
   Design tokens — mirrors design/design-tokens.md exactly.
   Available to any custom CSS written during the section build,
   so no ad-hoc hex/size/spacing ever gets typed by hand.
   ============================================================ */
:root{
  /* brand */
  --opal-peri:#B99EF9;
  --opal-peri-d:#9B7EE0;
  /* Accent for SMALL TEXT on light grounds. Same hue (257.8deg) and saturation
     as Deep Violet, darker only. Deep Violet itself measures 3.00:1 on Paper and
     3.25:1 on White — below the 4.5:1 AA floor for the eyebrow (12.5px) and the
     card link (15.2px). This clears it: 4.96 on Paper, 5.38 on White, 4.52 on
     Mist. Deep Violet is unchanged and still used everywhere it passes
     (button hover, large accents, accents on dark). */
  --opal-peri-ink:#7850D5;
  /* review stars — Google's own star gold, since these sit beside the Google G */
  --opal-star:#FBBC04;
  --opal-lav:#D5CFF4;
  /* grounds */
  --opal-ink:#0B0B10;
  --opal-ink-2:#15151D;
  --opal-line:#26262F;
  /* text */
  --opal-body-ink:#23232B;
  --opal-on-dark:#EDEBF5;
  --opal-on-dark-soft:#C9C6D6;
  --opal-stone:#6A6A78;
  /* light grounds */
  --opal-mist:#ECEAF4;
  --opal-paper:#F6F5FA;
  --opal-white:#FFFFFF;
  /* status */
  --opal-ok:#1F9D55;
  --opal-err:#D64545;
  /* layout */
  --opal-maxw:1200px;
  --opal-gut:clamp(20px,5vw,40px);
  --opal-sec-y:clamp(56px,9vw,104px);
  --opal-hdr-h:74px;
  /* radius */
  --opal-r-sm:8px;
  --opal-r-md:12px;
  --opal-r-lg:20px;
  --opal-r-pill:40px;

  /* brand lockup (icon + live wordmark) — replaces the old stacked PNG */
  --opal-lockup-gap:12px;
  --opal-lockup-icon:44px;      /* header */
  --opal-lockup-name:1.16rem;   /* 18.6px */
  --opal-lockup-sub:.638rem;    /* 55% of the name, per the lockup spec */
  --opal-lockup-icon-foot:56px;
  --opal-lockup-name-foot:1.375rem;   /* 22px */
  --opal-lockup-sub-foot:.756rem;
  --opal-lockup-icon-sm:38px;         /* mobile — both text lines are kept */
  --opal-lockup-name-sm:1rem;
  --opal-lockup-sub-sm:.55rem;
  /* effect + motion */
  --opal-sh:0 10px 30px rgba(0,0,0,.14);
  --opal-ease:cubic-bezier(.2,.7,.2,1);
  --opal-t-micro:150ms;
  --opal-t-reveal:500ms;

  --opal-font:'Poppins',system-ui,-apple-system,'Segoe UI',sans-serif;
}

/* Base type — Elementor Global Fonts drive the rest. */
body{
  font-family:var(--opal-font);
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}

/* Visible focus everywhere — never removed (a11y baseline). */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible{
  outline:2px solid var(--opal-lav);
  outline-offset:3px;
}

/* Skip link (Hello Elementor prints one; brand it). */
.skip-link:focus{
  background:var(--opal-ink);
  color:var(--opal-on-dark);
  outline:2px solid var(--opal-peri);
}

/* Honour reduced motion globally. */
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto !important;}
}

/* ============================================================
   Heading defaults.
   Elementor's Global Fonts define the tokens but do not apply them
   to bare h1–h3 — Hello Elementor's own theme.css does (weight 500,
   no tracking), which fights the type scale anywhere a heading is
   not an Elementor widget. Bind the bare tags to the same globals so
   the scale holds everywhere; Elementor widgets still override.
   ============================================================ */
h1{
  font-family:var(--e-global-typography-primary-font-family,'Poppins'),sans-serif;
  font-size:var(--e-global-typography-primary-font-size,clamp(2.1rem,5.2vw,3rem));
  font-weight:var(--e-global-typography-primary-font-weight,700);
  line-height:var(--e-global-typography-primary-line-height,1.06);
  letter-spacing:var(--e-global-typography-primary-letter-spacing,-.02em);
}
h2{
  font-family:var(--e-global-typography-h2-font-family,'Poppins'),sans-serif;
  font-size:var(--e-global-typography-h2-font-size,clamp(1.7rem,3.6vw,2.35rem));
  font-weight:var(--e-global-typography-h2-font-weight,700);
  line-height:var(--e-global-typography-h2-line-height,1.08);
  letter-spacing:var(--e-global-typography-h2-letter-spacing,-.01em);
}
h3{
  font-family:var(--e-global-typography-h3-font-family,'Poppins'),sans-serif;
  font-size:var(--e-global-typography-h3-font-size,clamp(1.15rem,2vw,1.4rem));
  font-weight:var(--e-global-typography-h3-font-weight,700);
  line-height:var(--e-global-typography-h3-line-height,1.12);
  letter-spacing:var(--e-global-typography-h3-letter-spacing,-.005em);
}
h4,h5,h6{
  font-family:'Poppins',sans-serif;
  font-weight:700;
  line-height:1.2;
}

/* ============================================================
   COMPONENT LAYER
   Every value below resolves to a token variable declared above.
   These are the pieces Elementor has no native control for
   (sticky scrolled state, transform on hover, fixed call bar).
   Applied via CSS classes set on Elementor containers/widgets.
   ============================================================ */

/* --- gutter utility: boxed containers get the token gutter ---
   Elementor boxed containers emit an inner `.e-con-inner` that carries the
   max-width, so layout classes must target that, not the outer element. --- */
.opal-wrap > .e-con-inner,
.opal-wrap:not(.e-con-boxed){ padding-inline:var(--opal-gut) !important; }

/* --- header: transparent over dark heroes, Ink once scrolled --- */
.opal-hdr{
  transition:background var(--opal-t-micro) var(--opal-ease),
             border-color var(--opal-t-micro) var(--opal-ease),
             backdrop-filter var(--opal-t-micro) var(--opal-ease);
  border-bottom:1px solid transparent;
}
.opal-hdr.opal-hdr--scrolled{
  background-color:rgba(11,11,16,.92) !important;
  -webkit-backdrop-filter:blur(10px);
  backdrop-filter:blur(10px);
  border-bottom-color:var(--opal-line);
}
/* keep the sticky header from jumping height on scroll */
.opal-hdr.opal-hdr--scrolled,
.opal-hdr{ min-height:var(--opal-hdr-h); }

/* --- nav submenu: descriptor line under each pillar ---
   Applies to both the desktop submenu (.elementor-nav-menu--main .sub-menu)
   and the mobile dropdown, so the descriptor always sits on its own line. --- */
.opal-nav .elementor-item-desc{
  display:block;
  font-size:.82rem;
  font-weight:400;
  color:var(--opal-on-dark-soft);
  margin-top:2px;
  white-space:normal;
}
.opal-nav .elementor-nav-menu--main .sub-menu a.elementor-sub-item,
.opal-nav .elementor-nav-menu--dropdown a.elementor-sub-item{
  display:block;
  line-height:1.35;
}
.opal-nav .elementor-nav-menu--main .sub-menu{ min-width:280px; }

/* --- mobile menu: full-screen Ink overlay (reference .mmenu) --- */
@media(max-width:1024px){
  .opal-nav .elementor-nav-menu--dropdown:not(.sub-menu){
    background-color:var(--opal-ink) !important;
    min-height:calc(100vh - var(--opal-hdr-h));
    padding:20px var(--opal-gut) 40px;
  }
  .opal-nav .elementor-nav-menu--dropdown a.elementor-item{
    font-size:1.35rem !important;
    font-weight:500 !important;
    padding:14px 0 !important;
    border-bottom:1px solid var(--opal-line);
  }
  .opal-nav .elementor-nav-menu--dropdown .elementor-sub-item{
    font-size:1rem !important;
    font-weight:400 !important;
    opacity:.8;
    padding-left:16px !important;
  }
}

/* --- buttons: the states Elementor's kit cannot express --- */
.elementor-button:hover{
  transform:translateY(-2px);
  box-shadow:0 8px 22px rgba(155,126,224,.4);
}
.elementor-button{
  transition:transform var(--opal-t-micro) var(--opal-ease),
             background-color .2s var(--opal-ease),
             color .2s var(--opal-ease),
             box-shadow .2s var(--opal-ease);
  will-change:transform;
}
.elementor-button:active{ transform:none; }

/* Secondary on dark — Lavender outline, fills on hover */
.opal-btn-ghost-d .elementor-button{
  background-color:transparent !important;
  border:1.5px solid var(--opal-lav) !important;
  color:var(--opal-lav) !important;
  box-shadow:none;
}
.opal-btn-ghost-d .elementor-button:hover{
  background-color:var(--opal-lav) !important;
  color:var(--opal-ink) !important;
  box-shadow:none;
  transform:translateY(-2px);
}

/* Secondary on light — Ink outline, fills on hover */
.opal-btn-ghost-l .elementor-button{
  background-color:transparent !important;
  border:1.5px solid var(--opal-ink) !important;
  color:var(--opal-ink) !important;
  box-shadow:none;
}
.opal-btn-ghost-l .elementor-button:hover{
  background-color:var(--opal-ink) !important;
  color:var(--opal-white) !important;
  box-shadow:none;
  transform:translateY(-2px);
}

/* --- mobile sticky call bar (reference .callbar) --- */
.opal-callbar{ display:none !important; }
@media(max-width:767px){
  .opal-callbar{
    display:flex !important;
    flex-direction:row !important;
    position:fixed;
    left:0; right:0; bottom:0;
    z-index:48;
    gap:1px;
    background-color:var(--opal-line);
  }
  /* Elementor's width-initial utility sets width:auto, and containers default
     to flex-wrap:wrap — so 50%+50%+gap wrapped onto two rows. Grow from a zero
     basis instead, and never wrap (reference .callbar is a 1fr 1fr grid). */
  .opal-callbar{ flex-wrap:nowrap !important; }
  .opal-callbar > .elementor-widget-button{
    flex:1 1 0 !important;
    width:auto !important;
    max-width:none !important;
    min-width:0 !important;
  }
  .opal-callbar .elementor-button{
    width:100%;
    justify-content:center;
    border-radius:0 !important;
    padding:15px !important;
    font-weight:600 !important;
    font-size:.95rem !important;
    transform:none !important;
    box-shadow:none !important;
  }
  /* clear the bar so it never covers page content */
  body{ padding-bottom:52px; }
}

/* --- reduced motion: no lifts, no reveals --- */
@media(prefers-reduced-motion:reduce){
  .elementor-button,
  .elementor-button:hover,
  .opal-btn-ghost-d .elementor-button:hover,
  .opal-btn-ghost-l .elementor-button:hover{
    transform:none !important;
    transition:none !important;
  }
  .opal-hdr{ transition:none !important; }
}

/* --- footer column grid (reference .foot .cols) --- */
.opal-foot-cols > .e-con-inner{
  display:grid !important;
  /* five columns since G2 split Services into Residential and
     Commercial & New Build (reference .foot .cols.cols5) */
  grid-template-columns:1.5fr 1fr 1.1fr 1.1fr 1.2fr;
  align-items:start;
}
.opal-foot-cols > .e-con-inner > .e-con{ width:100% !important; max-width:100% !important; }
@media(max-width:980px){
  .opal-foot-cols > .e-con-inner{ grid-template-columns:1fr 1fr; }
}
@media(max-width:767px){
  .opal-foot-cols > .e-con-inner{ grid-template-columns:1fr; }
}

/* --- header: nav sits on one line, logo keeps aspect --- */
.opal-hdr .elementor-widget-nav-menu{ flex-shrink:0; }
.opal-hdr .opal-nav .elementor-nav-menu--main > ul{ flex-wrap:nowrap; }

/* --- keyboard access to the Services submenu ---
   Elementor opens desktop submenus on hover only, so tabbing to "Services"
   left the three pillar links unreachable. :focus-within opens it for keyboard
   users exactly as hover does for pointer users (build brief §3: full nav
   operability including the Services dropdown). --- */
.opal-nav .elementor-nav-menu--main li:focus-within > ul.sub-menu{
  display:block !important;
  opacity:1 !important;
  visibility:visible !important;
}

/* ============================================================
   HERO  (section-tasks/home-01-hero.md · reference .hero)
   ============================================================ */
.opal-hero{ position:relative; overflow:hidden; }

/* media layer sits behind the content, full-bleed */
.opal-hero-media{
  position:absolute;
  inset:0;
  z-index:0;
  pointer-events:none;
}
/* Elementor's video widget wraps the <video> in an aspect-ratio box; the hero
   needs it to fill the container and crop instead. */
.opal-hero-media .elementor-widget-video,
.opal-hero-media .elementor-widget-container,
.opal-hero-media .elementor-wrapper{
  width:100%;
  height:100%;
  padding:0 !important;
}
.opal-hero-media .elementor-wrapper::before{ content:none !important; }
.opal-hero-media video{
  width:100%;
  height:100%;
  object-fit:cover;
  display:block;
}
/* Sub-page heroes use an image widget rather than the video. Elementor leaves
   the widget at the image's intrinsic height, so object-fit:cover has nothing
   to crop against and the backdrop stops short whenever the hero is taller than
   the image scales to — which is every width below roughly 900px. */
.opal-hero-media .elementor-widget-image,
.opal-hero-media .elementor-widget-image figure,
.opal-hero-media .elementor-widget-image a{
  width:100%;
  height:100%;
  margin:0;
}
.opal-hero-media img{
  width:100%;
  height:100%;
  object-fit:cover;
  display:block;
}
/* Left-to-right ink gradient so the copy always clears AA over the video.
   The reference ramp (.92 / .72@42% / .4) measured 3.93:1 on the lead at
   desktop widths — under the 4.5:1 AA floor for 19.5px regular text, at every
   frame of the loop. Sampling the whole 10s clip against candidate ramps, this
   is the lightest touch that clears it: lead 5.70:1, H1 9.54:1. The right-hand
   reveal of the video is preserved (.45 vs .40). */
.opal-hero-media::after{
  content:"";
  position:absolute;
  inset:0;
  background:linear-gradient(90deg,
    rgba(11,11,16,.93) 0%,
    rgba(11,11,16,.78) 50%,
    rgba(11,11,16,.45) 100%);
}
/* Between 768px and 1024px the horizontal ramp above still applies but the copy
   has widened enough to reach its light end, so on a bright photograph it stops
   clearing AA. Measured on the New Construction hero, the lightest of the set:
   the lead holds 5.19 at 1024px but drops to 4.26 at 900 and 3.18 at 768,
   against a 4.5 floor. This ramp restores it while still revealing the right of
   the frame. Below 768px the vertical ramp further down takes over instead. */
@media (min-width:768px) and (max-width:1024px){
  .opal-hero-media::after{
    background:linear-gradient(90deg,
      rgba(11,11,16,.92) 0%,
      rgba(11,11,16,.85) 50%,
      rgba(11,11,16,.74) 100%);
  }
}

.opal-hero-content{ position:relative; z-index:1; }

/* the periwinkle half of the H1 */
.opal-accent{ color:var(--opal-peri); }

@media(max-width:767px){
  /* portrait video + narrow viewport: gradient runs top-to-bottom instead so
     the text still sits on the dark end of it */
  .opal-hero-media::after{
    background:linear-gradient(180deg, rgba(11,11,16,.6), rgba(11,11,16,.88));
  }
}

/* --- entrance reveals --- */
/* Tokens put reveals at 300–500ms; Elementor's "fast" preset is 800ms. */
.opal-hero-content .animated{
  animation-duration:.5s;
  animation-timing-function:var(--opal-ease);
}

@media(prefers-reduced-motion:reduce){
  /* Elementor hides entrance-animated elements until they scroll into view.
     Under reduced motion nothing animates them back in, so un-hide explicitly
     rather than leaving the hero copy invisible. */
  .elementor-invisible{ visibility:visible !important; }
  .animated{ animation:none !important; }
}

/* measure: reference .hero h1{max-width:15ch} / .hero .lead{max-width:52ch}
   The measure has to land on the paragraph — Elementor sets max-width:100% on
   the widget wrapper, and a full-width lead runs into the light end of the
   hero gradient, where it drops below AA contrast over the video. */
.opal-hero-h1 .elementor-heading-title{ max-width:15ch; }
.opal-hero-content .opal-hero-lead .elementor-widget-container,
.opal-hero-content .opal-hero-lead p{ max-width:52ch; }

@media(max-width:767px){
  /* task home-01: buttons stack full-width on mobile so both stay comfortable
     tap targets rather than wrapping to content width */
  .opal-hero-cta{ flex-direction:column !important; align-items:stretch !important; }
  .opal-hero-cta > .elementor-widget-button{ width:100% !important; max-width:none !important; }
  .opal-hero-cta .elementor-button{ width:100%; justify-content:center; }
}

/* ============================================================
   TRUST BAR  (home-02 · reference .trust) — reusable global block
   ============================================================ */
.opal-trust{
  /* reference: border-block:1px solid var(--line) — top AND bottom */
  border-top:1px solid var(--opal-line);
  border-bottom:1px solid var(--opal-line);
}

/* 4 across, 2 up at tablet, 1 on mobile (reference breakpoints 980 / 760) */
.opal-trust-grid > .e-con-inner{
  display:grid !important;
  grid-template-columns:repeat(4,1fr);
  gap:24px;
  align-items:center;
}
.opal-trust-grid > .e-con-inner > .elementor-widget{
  width:100% !important;
  max-width:100% !important;
}
@media(max-width:980px){
  .opal-trust-grid > .e-con-inner{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:767px){
  .opal-trust-grid > .e-con-inner{ grid-template-columns:1fr; }
}

/* Row layout, icon gap and vertical centring are driven by the widget's own
   responsive controls (position / content_vertical_alignment), not by CSS —
   Elementor's mobile rules carry `!important` and would win otherwise. */
.opal-trust-item .elementor-icon-box-icon{
  flex:0 0 auto;
  margin:0 !important;
  display:flex;
  align-items:center;
}
.opal-trust-item .elementor-icon-box-content{ flex:1 1 auto; }
.opal-trust-item .elementor-icon-box-title{ margin:0; }
.opal-trust-item .elementor-icon-box-description{ margin:0; }
/* the uploaded SVGs carry stroke="currentColor", so Elementor's icon colour
   control drives them — no per-icon fill overrides needed */
.opal-trust-item .elementor-icon svg{
  width:1em;
  height:1em;
  fill:none;
}

/* reveal timing matches the hero */
.opal-trust .animated{
  animation-duration:.5s;
  animation-timing-function:var(--opal-ease);
}

/* ============================================================
   SECTION HEAD  (reference .shead) — reused by most sections
   ============================================================ */
.opal-shead{ max-width:64ch; }
.opal-shead.opal-shead--center{ margin-inline:auto; text-align:center; }

/* ============================================================
   SERVICE CARDS  (home-03 · reference .cards / .card)
   Reusable pattern: the Services hub reuses this exact card
   treatment with its own copy, so all styling lives here.
   ============================================================ */
.opal-cards > .e-con-inner{
  display:grid !important;
  grid-template-columns:repeat(3,1fr);
  gap:26px;
  align-items:stretch;
}
.opal-cards > .e-con-inner > .e-con{
  width:100% !important;
  max-width:100% !important;
}
@media(max-width:980px){
  .opal-cards > .e-con-inner{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:767px){
  .opal-cards > .e-con-inner{ grid-template-columns:1fr; }
}

/* the card itself */
.opal-card{
  background-color:var(--opal-white);
  border:1px solid var(--opal-mist);
  border-radius:var(--opal-r-lg);
  overflow:hidden;
  height:100%;
  transition:transform .25s var(--opal-ease), box-shadow .25s var(--opal-ease);
  will-change:transform;
}
.opal-card:hover{
  transform:translateY(-4px);
  box-shadow:var(--opal-sh);
}

/* image well — fixed 16:11 so the three cards align regardless of source ratio */
.opal-card-ph{
  aspect-ratio:16/11;
  overflow:hidden;
}
.opal-card-ph .elementor-widget-image,
.opal-card-ph .elementor-widget-container,
.opal-card-ph figure,
.opal-card-ph a{ height:100%; width:100%; display:block; }
.opal-card-ph img{
  width:100% !important;
  height:100% !important;
  object-fit:cover;
  display:block;
  transition:transform .5s var(--opal-ease);
}
.opal-card:hover .opal-card-ph img{ transform:scale(1.05); }

/* The body's flex-grow is set on the widget itself (_flex_size = grow), because
   Elementor's own `.e-con.e-flex{flex: var(--flex-grow)...}` rule outranks a
   plain class selector here. Left as a note so nobody re-adds it in CSS. */

/* tag chips (reference .card .sublist li) */
.opal-card-tags .elementor-icon-list-items{
  display:flex;
  flex-wrap:wrap;
  gap:8px;
  margin:0;
  padding:0;
}
.opal-card-tags .elementor-icon-list-item{
  margin:0 !important;
  padding:5px 11px !important;
  background-color:var(--opal-mist);
  border-radius:20px;
  font-size:.8rem;
  line-height:1.4;
  /* Reference uses Stone here, which measures 4.47:1 on Mist — just under AA
     for 12.8px text. Body Ink is an existing token and clears at 13.1:1; the
     chip still reads as recessive because of its size and pale pill. */
  color:var(--opal-body-ink);
}
.opal-card-tags .elementor-icon-list-item:after{ content:none !important; }
.opal-card-tags .elementor-icon-list-text{ padding:0 !important; color:inherit; }

/* "Get a Residential Quote →" — a text link, not a button */
.opal-card-go{ margin-top:auto !important; }
.opal-card-go .elementor-button{
  background-color:transparent !important;
  border:0 !important;
  padding:0 !important;
  border-radius:0 !important;
  color:var(--opal-peri-ink) !important;
  font-weight:500;
  box-shadow:none !important;
  transform:none !important;
}
.opal-card-go .elementor-button:hover{
  background-color:transparent !important;
  transform:none !important;
  box-shadow:none !important;
}
.opal-card-go .elementor-button-content-wrapper{ gap:6px; align-items:center; }
.opal-card-go .elementor-button-icon svg{
  width:16px; height:16px;
  transition:transform .2s var(--opal-ease);
}
.opal-card:hover .opal-card-go .elementor-button-icon svg{ transform:translateX(4px); }

/* reveal timing consistent with the rest of the page */
.opal-cards .animated,
.opal-shead .animated{
  animation-duration:.5s;
  animation-timing-function:var(--opal-ease);
}

@media(prefers-reduced-motion:reduce){
  .opal-card,
  .opal-card:hover,
  .opal-card-ph img,
  .opal-card:hover .opal-card-ph img,
  .opal-card:hover .opal-card-go .elementor-button-icon svg{
    transform:none !important;
    transition:none !important;
  }
  .opal-card:hover{ box-shadow:none; }
}

/* ============================================================
   WHY CHOOSE / FEATURE GRID  (home-04 · reference .feat / .f)

   Ground-agnostic on purpose. The same block appears on Home over
   Ink and on the Services hub over Paper (reference: index.html has
   it inside .dark, services.html does not). So the block itself
   carries no background — the page's slot container sets the ground
   and adds .opal-ondark, mirroring the reference's `.dark .feat .f`
   pattern. One global block, two grounds, zero duplication.
   ============================================================ */
.opal-feat > .e-con-inner{
  display:grid !important;
  grid-template-columns:repeat(4,1fr);
  gap:26px;
  align-items:stretch;
}
.opal-feat > .e-con-inner > .elementor-widget{
  width:100% !important;
  max-width:100% !important;
}
@media(max-width:980px){
  .opal-feat > .e-con-inner{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:767px){
  .opal-feat > .e-con-inner{ grid-template-columns:1fr; }
}

/* the card — light ground by default */
.opal-feat-card{
  height:100%;
  padding:28px;
  border-radius:var(--opal-r-md);
  background-color:var(--opal-white);
  border:1px solid var(--opal-mist);
  transition:transform .25s var(--opal-ease), box-shadow .25s var(--opal-ease);
  will-change:transform;
}
.opal-feat-card:hover{
  transform:translateY(-4px);
  box-shadow:var(--opal-sh);
}

/* …and on a dark ground (reference `.dark .feat .f`) */
.opal-ondark .opal-feat-card{
  background-color:var(--opal-ink-2);
  border-color:var(--opal-line);
}
/* Tokens: "On dark, use a 1px #26262F border instead of a shadow."
   So the dark lift is transform only — no shadow. */
.opal-ondark .opal-feat-card:hover{
  box-shadow:none;
  border-color:var(--opal-peri-d);
}

/* icon tile: 46x46 rounded square, periwinkle at 16% */
.opal-feat-card .elementor-icon{
  width:46px;
  height:46px;
  border-radius:12px;
  display:grid;
  place-items:center;
  padding:0 !important;
}
.opal-feat-card .elementor-icon svg{
  width:22px;
  height:22px;
  fill:none;
}
.opal-feat-card .elementor-icon-box-icon{ margin-bottom:16px !important; }

/* copy */
.opal-feat-card .elementor-icon-box-title{ margin:0 0 6px; }
.opal-feat-card .elementor-icon-box-description{ margin:0; }

/* reveal timing consistent with the rest of the page */
.opal-feat .animated,
.opal-whychoose .opal-shead .animated{
  animation-duration:.5s;
  animation-timing-function:var(--opal-ease);
}

@media(prefers-reduced-motion:reduce){
  .opal-feat-card,
  .opal-feat-card:hover{
    transform:none !important;
    transition:none !important;
    box-shadow:none;
  }
}

/* --- why-choose: text colours are ground-aware too ---
   These are NOT set on the widgets. A hard-coded colour would survive the
   ground switch, and the dark-ground text (#EDEBF5 / #C9C6D6) is effectively
   invisible on the white card used on the Services hub. Light is the default,
   .opal-ondark overrides — the same shape as the reference's `.dark .feat …`. */
.opal-whychoose .opal-eyebrow .elementor-heading-title{ color:var(--opal-peri-ink); }
.opal-whychoose .opal-h2 .elementor-heading-title{ color:var(--opal-body-ink); }
.opal-feat-card .elementor-icon-box-title,
.opal-feat-card .elementor-icon-box-title > span{ color:var(--opal-body-ink); }
.opal-feat-card .elementor-icon-box-description{ color:var(--opal-stone); }
/* Deep Violet on the 16% periwinkle tile over white measures 2.89:1 — under
   the 3:1 floor for a non-text graphic. The accessible accent clears it. */
.opal-feat-card .elementor-icon{ color:var(--opal-peri-ink); }
.opal-feat-card .elementor-icon svg{ stroke:currentColor; }

.opal-ondark .opal-whychoose .opal-eyebrow .elementor-heading-title{ color:var(--opal-peri); }
.opal-ondark .opal-whychoose .opal-h2 .elementor-heading-title{ color:var(--opal-on-dark); }
.opal-ondark .opal-feat-card .elementor-icon-box-title,
.opal-ondark .opal-feat-card .elementor-icon-box-title > span{ color:var(--opal-on-dark); }
.opal-ondark .opal-feat-card .elementor-icon-box-description{ color:var(--opal-on-dark-soft); }
.opal-ondark .opal-feat-card .elementor-icon{ color:var(--opal-peri); }

/* the ground switch itself should never animate */
.opal-feat-card{ transition-property:transform, box-shadow; }

/* ============================================================
   COLOUR-MATCHING STRIP  (home-05 · reference .cmatch / .chips)
   A rounded dark panel sitting on the light page — the one
   ownable differentiator, so it gets its own treatment.
   ============================================================ */
.opal-cmatch{
  background-image:linear-gradient(120deg, var(--opal-ink) 0%, #171326 100%);
  border-radius:var(--opal-r-lg);
  overflow:hidden;
}
.opal-cmatch > .e-con-inner,
.opal-cmatch.e-con-full{
  display:flex;
  flex-direction:row;
  align-items:center;
  justify-content:space-between;
  gap:40px;
  flex-wrap:wrap;
  padding:clamp(30px,5vw,56px);
}
.opal-cmatch-text{ max-width:52ch; }

/* swatch row — decorative silicone tones, not brand colours */
.opal-chips{
  display:flex !important;
  flex-direction:row !important;
  flex-wrap:nowrap !important;
  gap:10px;
  padding:0 !important;
  width:auto !important;
}
.opal-chip{
  width:40px !important;
  min-width:40px;
  height:40px;
  min-height:40px;
  border-radius:50%;
  border:2px solid rgba(255,255,255,.14);
  padding:0 !important;
  flex:0 0 auto !important;
}

@media(max-width:767px){
  /* reference: .cmatch .wrap-in stacks, left-aligned, tighter gap */
  .opal-cmatch > .e-con-inner,
  .opal-cmatch.e-con-full{
    flex-direction:column;
    align-items:flex-start;
    gap:22px;
  }
}

.opal-cmatch .animated{
  animation-duration:.5s;
  animation-timing-function:var(--opal-ease);
}

/* ============================================================
   OUR WORK STRIP  (home-06 · reference .gallery-strip)
   Reusable global block — also used on the Services hub, which
   places it on the same light ground, so colours stay on the
   widgets and remain editable in the Elementor UI.
   ============================================================ */
.opal-gallery-strip > .e-con-inner{
  display:grid !important;
  grid-template-columns:repeat(4,1fr);
  gap:14px;
}
.opal-gallery-strip > .e-con-inner > .elementor-widget{
  width:100% !important;
  max-width:100% !important;
}
/* reference switches to 2-up only at the mobile breakpoint — it stays
   4 across on tablet, unlike the card grids */
@media(max-width:767px){
  .opal-gallery-strip > .e-con-inner{ grid-template-columns:repeat(2,1fr); }
}

/* Kitchen (ki-09) ships three tiles, not four, so it needs its own column
   count. The reference sets grid-template-columns:repeat(3,1fr) inline, which
   outranks its own mobile 2-up rule and leaves three ~108px squares on a phone
   — too small to read a caulking joint, which is the point of the strip. So:
   3 across from 768 up, and at mobile 2 across with the third tile spanning
   both columns as a 16/9 banner. No orphan half-width tile, nothing under
   170px. */
.opal-gallery-strip--3 > .e-con-inner{ grid-template-columns:repeat(3,1fr); }
@media(max-width:767px){
  .opal-gallery-strip--3 > .e-con-inner{ grid-template-columns:repeat(2,1fr); }
  .opal-gallery-strip--3 > .e-con-inner > .elementor-widget:nth-child(3){
    grid-column:1 / -1;
  }
  .opal-gallery-strip--3 > .e-con-inner > .elementor-widget:nth-child(3) a{
    aspect-ratio:16 / 9;
  }
}

.opal-gs-item .elementor-widget-container,
.opal-gs-item figure{ height:100%; margin:0; }
.opal-gs-item a{
  display:block;
  position:relative;
  aspect-ratio:1;
  border-radius:var(--opal-r-md);
  overflow:hidden;
}
.opal-gs-item img{
  width:100% !important;
  height:100% !important;
  object-fit:cover;
  display:block;
  transition:transform .5s var(--opal-ease);
}
.opal-gs-item a:hover img{ transform:scale(1.07); }
/* keyboard users get the same affordance, plus a visible ring */
.opal-gs-item a:focus-visible img{ transform:scale(1.07); }

.opal-workstrip .animated{
  animation-duration:.5s;
  animation-timing-function:var(--opal-ease);
}

@media(prefers-reduced-motion:reduce){
  .opal-gs-item img,
  .opal-gs-item a:hover img,
  .opal-gs-item a:focus-visible img{
    transform:none !important;
    transition:none !important;
  }
}

/* ============================================================
   CTA BAND  (home-08 · reference .cta)

   A styling PATTERN, not a locked global block: the layout and
   look live here, the copy differs on every page. Insert the
   `cta-band` template, swap the text, and every instance still
   matches because the styling never travels with the copy.
   ============================================================ */
.opal-cta{
  position:relative;
  overflow:hidden;
  text-align:center;
}
/* faint periwinkle glow behind the heading */
.opal-cta::before{
  content:"";
  position:absolute;
  top:-40%;
  left:50%;
  transform:translateX(-50%);
  width:600px;
  height:600px;
  background:radial-gradient(circle, rgba(185,158,249,.22), transparent 62%);
  pointer-events:none;
  z-index:0;
}
.opal-cta > .e-con-inner{
  position:relative;
  z-index:1;
}
.opal-cta-h2 .elementor-heading-title{
  max-width:20ch;
  margin-inline:auto;
}
/* The measure has to land on the widget container and the paragraph — Elementor
   sets max-width:100% on the widget wrapper, which beats a plain class selector
   (same trap as the hero lead in home-01). */
.opal-cta .opal-cta-body .elementor-widget-container,
.opal-cta .opal-cta-body p{
  max-width:56ch;
  margin-inline:auto;
}
.opal-cta-buttons{
  justify-content:center !important;
}

.opal-cta .animated{
  animation-duration:.5s;
  animation-timing-function:var(--opal-ease);
}

@media(max-width:767px){
  /* same call as the hero: full-width stacked CTAs are easier to hit */
  .opal-cta-buttons{ flex-direction:column !important; align-items:stretch !important; }
  .opal-cta-buttons > .elementor-widget-button{ width:100% !important; max-width:none !important; }
  .opal-cta-buttons .elementor-button{ width:100%; justify-content:center; }
}

/* --- compact sub-page hero (contact-01 · reference .hero-sub) ---
   Same media/gradient treatment as the home hero, shorter. Contact uses an
   inline override in the reference (300–420px) rather than the default
   .hero-sub scale, because the quote form sits directly beneath it. */
.opal-hero--sub{
  min-height:clamp(340px,52vh,520px);
}
.opal-hero--compact{
  min-height:clamp(300px,42vh,420px);
}
/* still images use the same media layer as the video hero */
.opal-hero-media img{
  width:100%;
  height:100%;
  object-fit:cover;
  display:block;
}

/* ============================================================
   CONTACT — quote form + details  (contact-02 · reference .contact-grid)
   ============================================================ */
.opal-contact-grid > .e-con-inner{
  display:grid !important;
  grid-template-columns:1.15fr .85fr;
  gap:40px;
  align-items:start;
}
.opal-contact-grid > .e-con-inner > .e-con{
  width:100% !important;
  max-width:100% !important;
}
@media(max-width:980px){
  .opal-contact-grid > .e-con-inner{ grid-template-columns:1fr; }
}

/* --- the form card --- */
.opal-form-card{
  background-color:var(--opal-white);
  border:1px solid var(--opal-mist);
  border-radius:var(--opal-r-lg);
  box-shadow:var(--opal-sh);
}
/* two-up field rows collapse on mobile (reference .form .two) */
.opal-form .elementor-field-group{ margin-bottom:18px; }
.opal-form .elementor-field-label{
  font-size:.85rem;
  font-weight:500;
  color:#41414D;
  margin-bottom:6px;
}
.opal-form textarea.elementor-field-textual{ min-height:110px; resize:vertical; }
.opal-form .elementor-field-type-upload input[type="file"]{
  font-size:.85rem;
  color:var(--opal-stone);
}
.opal-form .elementor-button[type="submit"]{ width:100%; justify-content:center; }

/* The honeypot: hidden from sight, and taken out of the tab order / a11y tree
   by js/opal.js. Elementor puts a widget's custom classes on the INPUT, so the
   group is targeted via the `elementor-field-group-{custom_id}` class Elementor
   generates on the wrapper — a stable hook that survives edits in the UI. */
.opal-form .elementor-field-group-opal_hp{
  position:absolute !important;
  left:-9999px !important;
  top:auto !important;
  width:1px !important;
  height:1px !important;
  overflow:hidden;
  pointer-events:none;
}

.opal-form-note{
  font-size:.78rem;
  color:var(--opal-stone);
  text-align:center;
  margin-top:12px;
}

/* --- the contact details card (reference .info-card) --- */
.opal-info-card{
  background-color:var(--opal-ink);
  border-radius:var(--opal-r-lg);
}
.opal-info-blk{
  padding:18px 0;
  border-bottom:1px solid var(--opal-line);
}
.opal-info-blk:last-child{ border-bottom:0; }
.opal-info-lbl .elementor-heading-title{
  font-size:.75rem;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--opal-peri);
  font-weight:500;
}
.opal-info-val .elementor-heading-title{
  font-size:1.15rem;
  font-weight:500;
  color:var(--opal-on-dark);
  margin-top:6px;
}
.opal-info-val a:hover{ color:var(--opal-peri); }
.opal-info-sm p{
  font-size:.98rem;
  font-weight:400;
  color:var(--opal-on-dark-soft);
  margin-top:6px;
}

/* --- map (contact-03 · reference .mapwrap) --- */
.opal-mapwrap{
  border-radius:var(--opal-r-lg);
  overflow:hidden;
  border:1px solid var(--opal-mist);
  aspect-ratio:21/8;
}
/* Every wrapper in the chain has to carry the height, or the iframe falls back
   to its HTML default of 150px inside the aspect-ratio box. */
.opal-mapwrap > .e-con-inner,
.opal-mapwrap .elementor-widget-html,
.opal-mapwrap .elementor-widget-container,
.opal-mapwrap iframe{
  width:100%;
  height:100%;
  border:0;
  display:block;
}
@media(max-width:767px){
  .opal-mapwrap{ aspect-ratio:4/3; }
}

/* Links inside the dark contact card follow the on-dark scale, not the global
   link colour: Deep Violet is the accent for LIGHT grounds. Matches the
   reference's `.info-card .val a` treatment. */
.opal-info-card a{ color:var(--opal-on-dark-soft); }
.opal-info-card .opal-info-val a{ color:var(--opal-on-dark); }
.opal-info-card a:hover,
.opal-info-card a:focus-visible{ color:var(--opal-peri); }

/* ============================================================
   SPECIALISED SERVICES GRID  (services-03 · reference .sgrid)
   White chips on the Ink section — the reference deliberately
   keeps `.sgrid .s` light even inside `.dark`, so they pop.
   ============================================================ */
.opal-sgrid > .e-con-inner{
  display:grid !important;
  grid-template-columns:repeat(auto-fill, minmax(215px, 1fr));
  gap:16px;
  align-items:stretch;
}
.opal-sgrid > .e-con-inner > .elementor-widget{
  width:100% !important;
  max-width:100% !important;
}

.opal-sitem{
  height:100%;
  padding:20px;
  border-radius:var(--opal-r-md);
  background-color:var(--opal-white);
  border:1px solid var(--opal-mist);
  transition:transform .2s var(--opal-ease), box-shadow .2s var(--opal-ease), border-color .2s var(--opal-ease);
  will-change:transform;
}
.opal-sitem:hover{
  border-color:var(--opal-peri);
  transform:translateY(-2px);
  box-shadow:var(--opal-sh);
}

/* icon tile: 40x40, periwinkle at 16%, inverting to solid on hover */
.opal-sitem .elementor-icon{
  width:40px;
  height:40px;
  border-radius:10px;
  display:grid;
  place-items:center;
  padding:0 !important;
  transition:background-color .2s var(--opal-ease), color .2s var(--opal-ease);
}
.opal-sitem .elementor-icon svg{ width:20px; height:20px; fill:none; }
.opal-sitem:hover .elementor-icon{
  background-color:var(--opal-peri) !important;
  color:var(--opal-ink) !important;
}
.opal-sitem .elementor-icon-box-icon{ margin:0 !important; }

/* The chip is a white card sitting inside a dark section, so the copy needs its
   own colours — otherwise the title inherits the section's On-Dark and lands at
   about 1.1:1 on white, which is what the reference does. */
.opal-sitem .elementor-icon-box-title,
.opal-sitem .elementor-icon-box-title > span{
  color:var(--opal-body-ink);
  font-weight:500;
  font-size:.95rem;
  line-height:1.3;
  margin:0 0 2px;
  display:block;
}
.opal-sitem .elementor-icon-box-description{
  color:var(--opal-stone);
  font-size:.84rem;
  line-height:1.5;
  margin:0;
}

.opal-sgrid .animated{
  animation-duration:.5s;
  animation-timing-function:var(--opal-ease);
}

@media(prefers-reduced-motion:reduce){
  .opal-sitem,
  .opal-sitem:hover{ transform:none !important; transition:none !important; }
  .opal-sitem:hover{ box-shadow:none; }
}

/* ============================================================
   SPLIT FEATURE  (res-02/03 · reference .split)
   Text one side, image the other. Shared by all three pillar
   pages and the About page splits, so it lives here rather
   than being rebuilt per section.
   ============================================================ */
.opal-split > .e-con-inner{
  display:grid !important;
  grid-template-columns:1.05fr .95fr;
  gap:clamp(30px,5vw,64px);
  align-items:center;
}
.opal-split > .e-con-inner > .e-con{
  width:100% !important;
  max-width:100% !important;
}
/* reversed variant — image first (reference .split.rev) */
.opal-split--rev > .e-con-inner > .e-con:first-child{ order:2; }

@media(max-width:980px){
  .opal-split > .e-con-inner{ grid-template-columns:1fr; }
  /* on one column the text always leads, even when reversed */
  .opal-split--rev > .e-con-inner > .e-con:first-child{ order:0; }
}

/* the image side */
.opal-split-media{
  border-radius:var(--opal-r-lg);
  overflow:hidden;
  aspect-ratio:4/3;
  box-shadow:var(--opal-sh);
}
.opal-split-media .elementor-widget-image,
.opal-split-media .elementor-widget-container,
.opal-split-media figure{ height:100%; width:100%; margin:0; }
.opal-split-media img{
  width:100% !important;
  height:100% !important;
  object-fit:cover;
  display:block;
}
@media(max-width:980px){
  .opal-split-media{ aspect-ratio:16/10; }
}

/* tick list (reference .split ul.ticks) */
.opal-ticks .elementor-icon-list-items{
  display:grid;
  gap:12px;
  margin:0;
  padding:0;
}
.opal-ticks .elementor-icon-list-item{
  display:flex;
  align-items:flex-start;
  gap:12px;
  margin:0 !important;
  font-size:.98rem;
  line-height:1.45;
}
.opal-ticks .elementor-icon-list-icon{
  flex:0 0 auto;
  margin-top:3px;
  display:flex;
  /* Elementor's icon_color control sets `fill`, but these are STROKE icons
     drawn with currentColor — so the colour has to come from `color`, or the
     tick inherits the body text colour and the green never shows. */
  color:var(--opal-ok);
}
/* Elementor's per-widget rule paints `fill` on the icon and outranks a plain
   class selector, which fattens a stroke-only checkmark into a blob. These are
   outline icons: fill must stay off. */
.opal-ticks .elementor-icon-list-icon svg{
  width:20px;
  height:20px;
  fill:none !important;
}
.opal-ticks .elementor-icon-list-text{ padding:0 !important; }

.opal-split .animated{
  animation-duration:.5s;
  animation-timing-function:var(--opal-ease);
}

/* Pillar variant of the feature card: a check tile and a title, no description.
   The reference sets these titles at 1rem rather than the 1.05rem used on the
   Home/Services why-choose cards. */
.opal-feat-card--check .elementor-icon-box-description{ display:none; }
.opal-feat-card--check .elementor-icon-box-title{ margin-bottom:0; }
.opal-feat-card--check .elementor-icon svg{ fill:none !important; }

/* ---------- FAQ accordion (res-06 and the other pillar FAQs) ----------
   Elementor's nested-accordion renders native <details>/<summary>, which is
   the same markup the reference uses. Its own title icon is left unset so the
   reference's rotating "+" can be drawn as a pseudo-element instead. */
/* Elementor caps every widget with `.e-con.e-con > .e-con-inner > .elementor-widget
   { max-width:100% }` (0,4,0), so the measure cap has to outrank it by a class. */
.e-con.e-con > .e-con-inner > .opal-faq.opal-faq{
  max-width:820px;
  margin-inline:auto;
  width:100%;
}
.opal-faq .e-n-accordion{ display:block; }
.opal-faq .e-n-accordion-item{
  border-bottom:1px solid var(--opal-mist);
  padding:6px 0;
  background:none;
  border-radius:0;
}
.opal-ondark .opal-faq .e-n-accordion-item{ border-bottom-color:var(--opal-line); }

.opal-faq .e-n-accordion-item-title{
  list-style:none;
  cursor:pointer;
  padding:18px 40px 18px 0;
  position:relative;
  background:none;
  border:0;
  border-radius:0;
}
.opal-faq .e-n-accordion-item-title::-webkit-details-marker{ display:none; }
.opal-faq .e-n-accordion-item-title-header{ margin:0; }
.opal-faq .e-n-accordion-item-title-text{
  font-family:var(--opal-font);
  font-weight:500;
  font-size:1.05rem;
  line-height:1.4;
  color:var(--opal-ink);
  margin:0;
}
.opal-ondark .opal-faq .e-n-accordion-item-title-text{ color:var(--opal-on-dark); }

.opal-faq .e-n-accordion-item-title::after{
  content:"+";
  position:absolute;
  right:6px;
  top:50%;
  translate:0 -50%;
  font-size:1.5rem;
  line-height:1;
  color:var(--opal-peri-ink);
  transition:transform .2s var(--opal-ease);
}
.opal-ondark .opal-faq .e-n-accordion-item-title::after{ color:var(--opal-peri); }
.opal-faq .e-n-accordion-item[open] > .e-n-accordion-item-title::after{ transform:rotate(45deg); }

.opal-faq .e-n-accordion-item-title:focus-visible{
  outline:2px solid var(--opal-lav);
  outline-offset:3px;
  border-radius:var(--opal-r-sm);
}

/* the widget frames each answer container; the reference has no frame */
.opal-faq .e-n-accordion-item > .e-con{
  padding:0;
  border:0;
  background:none;
}
.opal-faq .e-n-accordion-item > .e-con p{
  margin:0;
  padding:0 0 20px;
  color:var(--opal-stone);
  font-size:.98rem;
  line-height:1.65;
}
.opal-ondark .opal-faq .e-n-accordion-item > .e-con p{ color:var(--opal-on-dark-soft); }

@media (prefers-reduced-motion:reduce){
  .opal-faq .e-n-accordion-item-title::after{ transition:none; }
}

/* ---------- Our Work gallery (work-02) ---------- */
.opal-gallery-filters{
  display:flex;
  flex-wrap:wrap;
  gap:10px;
  justify-content:center;
  margin-top:36px;
}
/* The kit styles every button (`.elementor-kit-5 button`, 0-1-1), which outranks
   a single class — these filters are chips, not primary buttons, so the
   component selector is doubled up to take precedence. */
.opal-gallery .opal-gallery-filter{
  border:1.5px solid #d9d5e8;
  background:var(--opal-white);
  color:var(--opal-ink);
  border-radius:var(--opal-r-pill);
  padding:9px 20px;
  font:inherit;
  font-size:.9rem;
  font-weight:500;
  line-height:1.5;
  cursor:pointer;
  transition:background .2s,color .2s,border-color .2s;
}
/* The kit also restyles button:hover/:focus with a periwinkle fill, so the chip
   has to hold its own background and colour through both states — as the
   reference does, where only the border reacts to hover. */
.opal-gallery .opal-gallery-filter:hover,
.opal-gallery .opal-gallery-filter:focus{
  background:var(--opal-white);
  color:var(--opal-ink);
  border-color:var(--opal-peri);
}
.opal-gallery .opal-gallery-filter:focus-visible{
  outline:2px solid var(--opal-lav);
  outline-offset:3px;
}
.opal-gallery .opal-gallery-filter.is-active,
.opal-gallery .opal-gallery-filter.is-active:hover,
.opal-gallery .opal-gallery-filter.is-active:focus{
  background:var(--opal-ink);
  color:var(--opal-white);
  border-color:var(--opal-ink);
}

/* CSS multi-column masonry, as the reference does */
.opal-gallery-grid{
  columns:3;
  column-gap:16px;
  margin-top:32px;
}
.opal-gallery-item{
  break-inside:avoid;
  margin:0 0 16px;
  border-radius:var(--opal-r-md);
  overflow:hidden;
  position:relative;
}
.opal-gallery-item[hidden]{ display:none; }
.opal-gallery-item img{
  width:100%;
  height:auto;
  display:block;
  transition:transform .5s var(--opal-ease);
}
.opal-gallery-item:hover img{ transform:scale(1.05); }
.opal-gallery-item figcaption{
  position:absolute;
  left:0;
  right:0;
  bottom:0;
  padding:26px 16px 12px;
  color:#fff;
  font-size:.85rem;
  background:linear-gradient(transparent,rgba(0,0,0,.7));
  opacity:0;
  transition:opacity .25s;
}
.opal-gallery-item:hover figcaption{ opacity:1; }
/* Captions reveal on hover, which never fires on a touch screen — show them
   outright where there is no hover so the descriptions are not mobile-only
   dead weight. */
@media (hover:none){
  .opal-gallery-item figcaption{ opacity:1; }
}

/* the live region only needs to speak, not occupy space */
.opal-gallery-status{
  position:absolute;
  width:1px;
  height:1px;
  margin:-1px;
  padding:0;
  overflow:hidden;
  clip-path:inset(50%);
  white-space:nowrap;
  border:0;
}

@media(max-width:980px){
  .opal-gallery-grid{ columns:2; }
}
@media(max-width:760px){
  .opal-gallery-grid{ columns:1; }
}
@media(prefers-reduced-motion:reduce){
  .opal-gallery-item img{ transition:none; }
  .opal-gallery-item:hover img{ transform:none; }
  .opal-gallery-item figcaption{ transition:none; }
}

/* The skip-link target must not draw a focus ring when it receives focus
   programmatically — the ring belongs on real interactive elements. */
.opal-main:focus{ outline:none; }

/* ---------- header/footer logo aspect ratio ----------
   The logo widget sits in a flex row with the nav, phone and CTA. It defaults to
   flex-shrink:1 with min-width:0, so once those compete for space the widget is
   squeezed narrower than the image — and `max-width:100%` then clamps the image
   to that squeezed width, beating `width:auto`. The square 1080×1080 mark was
   rendering 22×40 at 1280px and 12×40 at 768px: a distorted sliver.
   Hold the widget at its natural width and let the image size from its height. */
.opal-hdr .opal-lockup,
.opal-foot .opal-lockup{
  flex:0 0 auto;
}
.opal-hdr .opal-lockup img,
.opal-foot .opal-lockup img{
  max-width:none;
  width:auto;
}

/* The card CTA is a text-style Elementor button: zero padding and a line-height
   equal to its font-size, so the tap target was only 16px tall — under the 24px
   WCAG 2.2 (2.5.8) minimum. Grow the box, not the type. Every card gains the
   same 8px so the link baselines stay aligned across a row. */
.opal-card-go .elementor-button{
  display:inline-flex;
  align-items:center;
  min-height:24px;
}

/* ---------- video pause control (WCAG 2.2.2) ---------- */
/* doubled up: the kit styles every button (`.elementor-kit-5 button`, 0-1-1)
   with 14px/28px padding, which stretched this to 59px wide */
.opal-video-toggle.opal-video-toggle{
  position:absolute;
  right:16px;
  bottom:16px;
  z-index:4;
  width:40px;
  height:40px;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  padding:0;
  border:1px solid rgba(255,255,255,.38);
  border-radius:50%;
  /* solid enough to guarantee contrast over any frame of the clip */
  background:rgba(11,11,16,.74);
  color:#fff;
  cursor:pointer;
  transition:background .2s,border-color .2s;
}
.opal-video-toggle.opal-video-toggle:hover,
.opal-video-toggle.opal-video-toggle:focus{
  background:rgba(11,11,16,.88);
  border-color:rgba(255,255,255,.6);
  color:#fff;
}
.opal-video-toggle.opal-video-toggle:focus-visible{
  outline:2px solid var(--opal-lav);
  outline-offset:3px;
}
.opal-video-toggle.opal-video-toggle svg{
  width:16px;
  height:16px;
  fill:currentColor;
}

/* the process clip sits where a split image would, so it is framed the same way */
.opal-video-band{ position:relative; }
.opal-split-media video{
  width:100%;
  height:100%;
  object-fit:cover;
  display:block;
}
.opal-split-media .elementor-widget-video,
.opal-split-media .elementor-wrapper,
.opal-split-media .elementor-video-container{
  width:100%;
  height:100%;
}
.opal-split-media .elementor-wrapper::before{ content:none !important; }

/* ---------- home-01 hero: copy left, quote form right ----------
   The hero keeps its video, ink gradient, type and buttons exactly as built —
   this only splits the content container into two columns and drops the form
   card into the right one. */
.opal-hero-split > .e-con-inner{
  display:grid !important;
  grid-template-columns:55fr 45fr;
  gap:clamp(28px,4vw,56px);
  align-items:center;
}

/* the left column is the original stack, untouched */
.opal-hero-copy{
  display:flex;
  flex-direction:column;
  align-items:flex-start;
}

/* white card so the form stays legible over the video */
.opal-hero-form{
  padding:clamp(22px,2.6vw,30px);
  /* the card is on a light ground of its own — undo the hero's on-dark colours */
  color:var(--opal-ink);
}
.opal-hero-form .opal-hero-form-title{ margin:0 0 16px; }

/* the hero grew to fit the form, so the fixed 120/60 block padding is no longer
   doing the work — even it out and let min-height follow the content */
.opal-hero-split{
  padding-block:clamp(104px,12vh,132px) clamp(56px,8vh,72px) !important;
}

@media(max-width:767px){
  /* stack: copy first, form card beneath — the brief's order */
  .opal-hero-split > .e-con-inner{
    grid-template-columns:1fr;
    gap:28px;
  }
  .opal-hero-split{
    padding-block:clamp(92px,14vh,112px) clamp(40px,7vh,56px) !important;
  }
}

/* Adding the form doubled this hero's height (about 700px to 1675px at 390px
   wide), which stretches the mobile vertical scrim over far more page — so the
   copy, sitting in the upper third, landed on a lighter part of the ramp and the
   lead fell to 4.21:1, under the 4.5 floor. Steepen the ramp for this hero only;
   the other heroes are shorter and still clear it on the tuned .6/.88. */
@media(max-width:767px){
  .opal-hero--form .opal-hero-media::after{
    background:linear-gradient(180deg, rgba(11,11,16,.70), rgba(11,11,16,.90));
  }
}

/* ---------- home-04b · Testimonials ----------
   Not in the reference site; built from the design system so it reads as part of
   the same family — same card shell, radius, border, hover lift and stagger as
   .opal-card / .opal-feat-card. */
.opal-reviews-demo-flag{
  margin:0 auto 20px;
  max-width:820px;
  padding:10px 14px;
  border:1px dashed var(--opal-peri-d);
  border-radius:var(--opal-r-sm);
  background:rgba(185,158,249,.10);
  color:var(--opal-ink);
  font-size:.85rem;
  text-align:center;
}

.opal-reviews-badge{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:8px;
  flex-wrap:wrap;
  margin:14px 0 0;
  font-size:.92rem;
  color:var(--opal-ink);
}
.opal-reviews-badge-rating{ font-weight:700; }
.opal-reviews-badge-text{ color:var(--opal-stone); }

.opal-stars{ display:inline-flex; gap:2px; line-height:0; }
.opal-stars svg{ width:15px; height:15px; fill:var(--opal-star); }

.opal-reviews-grid{
  display:grid;
  grid-template-columns:repeat(3,1fr);
  gap:26px;
  margin-top:32px;
}

.opal-review-card{
  display:flex;
  flex-direction:column;
  gap:14px;
  margin:0;
  padding:clamp(20px,2.4vw,26px);
  background-color:var(--opal-white);
  border:1px solid var(--opal-mist);
  border-radius:var(--opal-r-lg);
  box-shadow:var(--opal-sh);
  height:100%;
  transition:transform .25s var(--opal-ease), box-shadow .25s var(--opal-ease);
  will-change:transform;
}
.opal-review-card:hover{ transform:translateY(-4px); }

.opal-review-quote{
  margin:0;
  font-size:.98rem;
  line-height:1.65;
  color:var(--opal-ink);
  /* the card carries the attribution, so no decorative quote marks */
}

.opal-review-footer{
  display:flex;
  align-items:center;
  gap:12px;
  margin-top:auto;
  padding-top:6px;
}
.opal-review-avatar{
  flex:0 0 auto;
  width:38px;
  height:38px;
  border-radius:50%;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  background:rgba(185,158,249,.20);
  color:var(--opal-peri-ink);
  font-weight:700;
  font-size:.95rem;
}
.opal-review-who{ display:flex; flex-direction:column; line-height:1.3; }
.opal-review-name{ font-weight:500; font-size:.92rem; color:var(--opal-ink); }
.opal-review-suburb{ font-size:.82rem; color:var(--opal-stone); }
.opal-review-via{
  margin-left:auto;
  display:inline-flex;
  align-items:center;
  gap:5px;
  font-size:.76rem;
  color:var(--opal-stone);
  white-space:nowrap;
}

@media(max-width:980px){
  .opal-reviews-grid{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:760px){
  .opal-reviews-grid{ grid-template-columns:1fr; }
  .opal-review-via{ margin-left:0; width:100%; }
  .opal-review-footer{ flex-wrap:wrap; }
}
@media(prefers-reduced-motion:reduce){
  .opal-review-card{ transition:none; }
  .opal-review-card:hover{ transform:none; }
}

/* ============================================================
   HEADER — reconciliation with the reference header
   (.hdr / .nav / .sub / .hdr-cta / .burger / .mmenu / .callbar)
   ============================================================ */

/* reference .nav a{opacity:.9} / .nav a:hover{opacity:1} */
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li > a.elementor-item{
  opacity:.9;
  transition:opacity .2s var(--opal-ease), color .2s var(--opal-ease);
}
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li > a.elementor-item:hover,
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li > a.elementor-item:focus-visible{
  opacity:1;
}

/* --- Services dropdown: fade + slide, not an instant display toggle ---
   Elementor shows submenus by flipping display, which cannot transition. Keep it
   displayed and drive open/closed with opacity + visibility + translate, exactly
   as the reference .sub does. */
.opal-hdr .opal-nav .elementor-nav-menu--main ul.sub-menu{
  display:block !important;
  position:absolute;
  top:calc(100% + 14px) !important;
  /* Elementor positions submenus with its own `left`, so this needs to win to
     centre the panel under "Services" the way the reference does */
  left:50% !important;
  right:auto !important;
  transform:translateX(-50%) translateY(8px);
  padding:10px;
  opacity:0;
  visibility:hidden;
  pointer-events:none;
  transition:opacity .2s var(--opal-ease), transform .2s var(--opal-ease), visibility .2s var(--opal-ease);
}
/* The 14px offset above is a visual gap, not a dead zone. Without this bridge the
   pointer leaves the <li> on its way down to the panel, `li:hover` goes false and
   the panel closes before any sub-item can be clicked. The bridge is a child of
   the panel, which is a child of the <li>, so hovering it keeps `li:hover` true —
   CSS :hover follows the DOM, not the geometry. */
.opal-hdr .opal-nav .elementor-nav-menu--main ul.sub-menu::before{
  content:"";
  position:absolute;
  left:0;
  right:0;
  top:-14px;
  height:14px;
}
/* Elementor's post CSS puts its own "distance from menu" margin on the panel:
   `... .elementor-element-hdrnav .elementor-nav-menu--main > .elementor-nav-menu
   > li > .elementor-nav-menu--dropdown{margin-top:14px!important}` — 0-6-1 and
   !important. Stacked on the `top` above it made the drop 27px, wider than the
   hover bridge, so the pointer fell out of the <li> on the way down. This
   selector is 0-6-2, so it wins and the panel's offset has one source. */
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li > ul.sub-menu.elementor-nav-menu--dropdown{
  margin-top:0 !important;
}
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li:hover > ul.sub-menu,
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li:focus-within > ul.sub-menu,
/* set by opal.js on touch devices, where :hover cannot be expressed — the first
   tap on "Services" opens the panel, the second follows the link */
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li.opal-sub-open > ul.sub-menu{
  opacity:1 !important;
  visibility:visible !important;
  pointer-events:auto;
  transform:translateX(-50%) translateY(0);
}
/* reference .sub a{border-radius:8px} — the panel radius must not bleed onto the
   first and last items */
.opal-hdr .opal-nav .elementor-nav-menu--main ul.sub-menu a.elementor-sub-item{
  border-radius:8px !important;
  padding:10px 12px;
}
.opal-hdr .opal-nav .elementor-nav-menu--main ul.sub-menu a.elementor-sub-item:hover,
.opal-hdr .opal-nav .elementor-nav-menu--main ul.sub-menu a.elementor-sub-item:focus-visible{
  background-color:rgba(185,158,249,.12);
}

@media(prefers-reduced-motion:reduce){
  .opal-hdr .opal-nav .elementor-nav-menu--main ul.sub-menu{ transition:none; }
}

/* ---------- the burger runs the whole tablet band ----------
   The desktop bar never fitted between 768 and 1024: at 820px the header button
   already ran to x=894 with the OLD 40px logo, and to 1045 with the lockup —
   brand + nav + phone + button needs ~930px inside a 740px content box. The nav
   widget's dropdown breakpoint is now `tablet`, and every header rule that was
   keyed to 767 moves with it. */
/* --- mobile + tablet --- */
@media(max-width:1024px){
  /* the reference keeps the quote button in the bar; only the nav and the
     phone number drop out */
  .opal-hdr .elementor-widget-button{ display:block !important; }

  /* reference .mmenu: a full-screen Ink overlay that slides in from the right */
  /* Elementor's own rule for this widget is 0-4-0 AND !important, so the
     override has to out-specify it, not just shout louder */
  .opal-hdr .elementor-widget-nav-menu.opal-nav .elementor-nav-menu--dropdown.elementor-nav-menu__container:not(.sub-menu){
    position:fixed !important;
    inset:0 !important;
    width:100% !important;
    max-height:none !important;
    height:100dvh !important;
    border-radius:0 !important;
    /* Elementor gives the dropdown a 14px "distance from menu" margin, which
       pushes a fixed, inset:0 overlay down and leaves a strip of hero showing */
    margin:0 !important;
    padding:calc(var(--opal-hdr-h) + 20px) var(--opal-gut) 40px !important;
    overflow-y:auto;
    z-index:49;
  }
  /* reference bar order: logo · quote button · burger at the far right.
     The nav widget (which holds the burger) sits before the button in the DOM,
     so it has to be reordered visually. */
  .opal-hdr .opal-lockup{ order:1; }
  /* the CTA group (phone + button) is its own container, not a bare widget */
  .opal-hdr .elementor-element-hdrcta{ order:2; margin-left:auto; }
  .opal-hdr .elementor-widget-nav-menu{ order:3; flex:0 0 auto; }

  /* logo + quote button + burger stay on one 74px row, as in the reference;
     without this the button wraps and the bar grows to 112px */
  .opal-hdr > .e-con-inner,
  .opal-hdr .opal-wrap > .e-con-inner{
    flex-wrap:nowrap !important;
    gap:12px;
  }
  /* the reference keeps the button at full size on mobile (190x56) and it still
     fits at 390px: 40 logo + 190 button + 42 burger + gaps + 40 gutters = 336 */
  .opal-hdr .elementor-widget-button .elementor-button{
    white-space:nowrap;
  }
  /* Elementor tints the toggle; the reference burger has no background */
  .opal-hdr .elementor-menu-toggle{
    background-color:transparent !important;
    padding:8px;
  }

  /* The reference overlay is a sibling BELOW the header (.mmenu z-index 49,
     .hdr z-index 50), so the bar — logo, quote button, close — stays on top of
     it. Here the overlay is nested inside the nav widget, so the bar items have
     to be lifted above it explicitly. */
  .opal-hdr .opal-lockup,
  .opal-hdr .elementor-widget-button,
  .opal-hdr .elementor-menu-toggle{
    position:relative;
    z-index:60;
  }

  /* Elementor draws a 1px border around the dropdown panel. The reference
     overlay has none, and here it inset the whole menu by 1px (buttons came out
     348 wide against the reference's 350). */
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown{ border:0 !important; }

  /* the two CTAs at the foot of the menu (reference .mmenu .btn) */
  /* Measured against the reference: these are FULL-WIDTH buttons (350px of the
     390 viewport), 65px tall, centred, with the menu's own 1.35rem type — not
     content-width pills. */
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown .opal-menu-cta > a.elementor-item,
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown .opal-menu-call > a.elementor-item{
    display:flex !important;
    width:100%;
    align-items:center;
    justify-content:center;
    /* 26px clear of the last link, as in the reference (its 20px margin plus the
       .mmenu 6px gap). Margins collapse through the <li>, so it is set as one. */
    margin-top:26px;
    padding:14px 0 !important;
    border:1.5px solid transparent;
    /* Elementor gives the dropdown's LAST link a bottom radius of its own; that
       rule is later and wins on the Call button unless this is forced. */
    border-radius:var(--opal-r-pill) !important;
    font-size:1.35rem !important;
    font-weight:500 !important;
    line-height:1.6;
    text-align:center;
  }
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown .opal-menu-cta > a.elementor-item{
    background-color:var(--opal-peri);
    color:var(--opal-ink) !important;
  }
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown .opal-menu-call > a.elementor-item{
    margin-top:26px;
    border-color:var(--opal-lav);
    /* reference: lavender border, on-dark label (#EDEBF5) — not lavender text */
    color:var(--opal-on-dark) !important;
  }
}

/* these two exist only for the mobile overlay */
@media(min-width:1025px){
  .opal-hdr .opal-nav .opal-menu-cta,
  .opal-hdr .opal-nav .opal-menu-call{ display:none !important; }
}

/* ---------- header: restore the reference's three-part space-between ----------
   Elementor containers default to width:100%, so the CTA group was 596px wide
   and swallowed the free space — leaving `space-between` nothing to distribute
   and shoving the nav hard left (nav.left 137 vs the reference's 302).
   All three children must size to their content for the reference layout to
   reproduce: logo | nav | phone + button, evenly spaced. */
.opal-hdr > .e-con-inner > .elementor-element,
.opal-hdr .opal-wrap > .e-con-inner > .elementor-element{
  flex:0 0 auto !important;
  width:auto !important;
}
/* reference .nav a{padding:6px 0} — no horizontal padding, the 28px gap does
   the spacing */
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li > a.elementor-item{
  padding-inline:0;
}

/* ---------- mobile menu: slide in from the right (reference .mmenu) ----------
   Elementor shows the dropdown by flipping `display`, which cannot animate. Keep
   it laid out at all times and drive open/closed with transform + visibility, so
   it slides in over .3s exactly as the reference does. */
@media(max-width:1024px){
  .opal-hdr .elementor-widget-nav-menu.opal-nav .elementor-menu-toggle + .elementor-nav-menu--dropdown.elementor-nav-menu__container{
    display:block !important;
    transform:translateX(100%);
    visibility:hidden;
    pointer-events:none;
    transition:transform .3s var(--opal-ease), visibility .3s var(--opal-ease);
  }
  .opal-hdr .elementor-widget-nav-menu.opal-nav .elementor-menu-toggle.elementor-active + .elementor-nav-menu--dropdown.elementor-nav-menu__container{
    transform:none;
    visibility:visible;
    pointer-events:auto;
  }
}
@media(max-width:1024px) and (prefers-reduced-motion:reduce){
  .opal-hdr .elementor-widget-nav-menu.opal-nav .elementor-menu-toggle + .elementor-nav-menu--dropdown.elementor-nav-menu__container{
    transition:none;
  }
}
/* body scroll lock while the overlay is open (reference sets body overflow) */
body.opal-menu-open{ overflow:hidden; }

/* ---------- mobile menu: match the reference's expanded sub-links ----------
   The reference lists the three Services pages inline, each prefixed with an em
   dash, rather than hiding them behind a toggle — on a phone that saves a tap
   and exposes the pillar pages. */
@media(max-width:1024px){
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown ul.sub-menu{
    display:block !important;
    position:static !important;
    opacity:1 !important;
    visibility:visible !important;
    transform:none !important;
    pointer-events:auto !important;
    background:none !important;
    border:0 !important;
    box-shadow:none !important;
    padding:0 !important;
    min-width:0 !important;
    min-height:0 !important;
    height:auto !important;
  }
  /* reference .mmenu a.sub-link */
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown ul.sub-menu a.elementor-sub-item{
    font-size:1rem !important;
    font-weight:400 !important;
    opacity:.8;
    padding:14px 0 14px 16px !important;
    border-bottom:1px solid var(--opal-line);
    border-radius:0 !important;
  }
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown ul.sub-menu a.elementor-sub-item::before{
    content:"— ";
  }
  /* Row height. The reference's .mmenu links inherit line-height 1.6 (34.56px at
     1.35rem, 25.6px at 1rem) for 64px / 55px rows; Elementor's nav-menu
     typography pins line-height to 20px, which gave 49px / 51px rows here. */
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown a.elementor-item{
    line-height:1.6 !important;
  }
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown ul.sub-menu a.elementor-sub-item{
    line-height:1.6 !important;
  }
  /* The reference overlay is a flex column with gap:6px. This list is a nested
     <ul>, so the same 6px has to come from the rows themselves. */
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown li{ margin-bottom:6px; }
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown ul.sub-menu{ margin-top:6px !important; }
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown ul.sub-menu > li:last-child{ margin-bottom:0; }

  /* the descriptor line is desktop-only; the reference's mobile list is titles */
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown .elementor-item-desc{ display:none !important; }
  /* always expanded, so the caret would be a control that does nothing */
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown .sub-arrow,
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown .fa-caret-down{ display:none !important; }

  /* the reference has no filled block on the current page's link */
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown a.elementor-item.elementor-item-active,
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown a.elementor-item:focus,
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown a.elementor-item:hover{
    background-color:transparent !important;
    border-radius:0 !important;
  }
}

/* ---------- header: metric parity with the reference ----------
   Measured against the reference at 1280 and matched element by element.
   The reference sets no line-height on .btn or .nav a, so both inherit
   body{line-height:1.6}. Elementor forces line-height:1 on buttons and 1.3 on
   menu items, which made the button 47px instead of 56px and each nav link
   32px instead of 36px — visibly smaller than the reference. */
.opal-hdr .elementor-element-hdrcta .elementor-button{
  line-height:1.6;
  display:inline-flex;          /* reference .btn{display:inline-flex} */
  align-items:center;
  justify-content:center;
}
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li > a.elementor-item{
  line-height:1.6;
}
/* reference .brand{height:44px} */
.opal-hdr .opal-lockup{
  min-height:44px;
  display:flex;
  align-items:center;
}
/* reference .nav{display:flex;align-items:center;gap:28px} */
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu{
  align-items:center;
  gap:28px;
}
/* Elementor already spaces items with margin:0 14px on the anchor
   (--e-nav-menu-horizontal-menu-item-margin). Left in place it stacks with the
   flex gap above and doubles the spacing to 56px, widening the nav from 397px
   to 576px. The reference spaces purely with the container gap, so zero the
   per-item margin and let the gap do it. */
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li{
  margin:0;
}
.opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li > a.elementor-item{
  margin-inline:0;
}

/* call bar: reference links are padding:15px with the inherited 1.6 line-height,
   giving a 54px bar; Elementor's tighter line-height made it 48px */
/* reference .callbar a has padding:15px, the inherited 1.6 line-height and no
   border — the global .btn border was adding 3px, making the bar 57px not 54 */
.opal-callbar a,
.opal-callbar .elementor-button{
  line-height:1.6;
  border:0;
}

/* ---------- brand lockup ----------
   The old brand was a single stacked PNG whose wordmark was unreadable at 40px.
   This is the icon plus live Poppins text, so the words stay crisp at any size
   and can be read by search engines and screen readers. The container renders as
   the <a> (html_tag), which is why nothing inside it links. */
.opal-lockup{
  display:flex;
  flex-direction:row;
  align-items:center;
  gap:var(--opal-lockup-gap);
  text-decoration:none;
}
.opal-lockup .opal-lockup-icon img{
  display:block;
  height:var(--opal-lockup-icon);
  width:auto;
  max-width:none;
}
/* the two lines are one block, left-aligned, so "SERVICES" starts under "OPAL" */
.opal-lockup .opal-lockup-text{
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  justify-content:center;
}
.opal-lockup .opal-lockup-name{
  font-family:var(--opal-font);
  font-weight:700;
  font-size:var(--opal-lockup-name);
  line-height:1.08;
  letter-spacing:-.01em;
  text-transform:uppercase;
  color:var(--opal-on-dark);
  white-space:nowrap;
}
.opal-lockup .opal-lockup-sub{
  font-family:var(--opal-font);
  font-weight:500;
  font-size:var(--opal-lockup-sub);
  line-height:1.2;
  letter-spacing:.35em;
  text-transform:uppercase;
  color:var(--opal-peri);
  white-space:nowrap;
}
/* Elementor wraps the HTML widget; it must not add its own box or the wordmark
   sits off-centre against the icon */
.opal-lockup .elementor-widget-html,
.opal-lockup .elementor-widget-image{
  flex:0 0 auto;
  width:auto;
  margin:0;
}
.opal-lockup .elementor-widget-image .elementor-widget-container,
.opal-lockup .elementor-widget-html .elementor-widget-container{
  display:flex;
  align-items:center;
}
/* the whole lockup is the link, so the focus ring belongs on it */
.opal-lockup:focus-visible{
  outline:2px solid var(--opal-lav);
  outline-offset:4px;
  border-radius:var(--opal-r-sm);
}

/* footer runs a touch larger; the 16px below it was the old logo widget's own
   margin, kept so the blurb does not ride up against the wordmark */
.opal-foot .opal-lockup{ margin-bottom:16px; }
.opal-foot .opal-lockup .opal-lockup-icon img{ height:var(--opal-lockup-icon-foot); }
.opal-foot .opal-lockup .opal-lockup-name{ font-size:var(--opal-lockup-name-foot); }
.opal-foot .opal-lockup .opal-lockup-sub{ font-size:var(--opal-lockup-sub-foot); }

@media(max-width:767px){
  .opal-lockup .opal-lockup-icon img,
  .opal-foot .opal-lockup .opal-lockup-icon img{ height:var(--opal-lockup-icon-sm); }
  .opal-lockup .opal-lockup-name,
  .opal-foot .opal-lockup .opal-lockup-name{ font-size:var(--opal-lockup-name-sm); }
  .opal-lockup .opal-lockup-sub,
  .opal-foot .opal-lockup .opal-lockup-sub{ font-size:var(--opal-lockup-sub-sm); }
  .opal-lockup{ gap:8px; }

  /* Refitting the mobile bar for the lockup.
     The old brand was a 40px square; the lockup is ~172px. Lockup + the 190px
     quote button + the burger comes to 416px inside a 390px bar, and the burger
     was landing at x=410 — off-screen and unreachable. Trimming the button and
     the gaps still left it 6px over, and it fails outright at 360px.

     So the header's quote button steps aside below 768. The CTA is not lost:
     the sticky call bar carries "Request a Quote" on every screen, and the menu
     repeats it at full width. Losing a duplicate CTA costs less than a brand
     nobody can read or a burger nobody can reach. */
  .opal-hdr .elementor-element-hdrcta{ display:none !important; }
  .opal-hdr .opal-wrap > .e-con-inner{ gap:12px; }
  .opal-hdr .elementor-menu-toggle{ padding:6px; margin-left:auto; }
}

/* ---------- Thank You page (ty-01…ty-04) ----------
   The page the quote form redirects to. It was a plain Gutenberg page whose
   title collided with the sticky header and which carried no links at all; it is
   now built from the same sections as the rest of the site. Only the row layout
   of the contact blocks is new — `.opal-info-blk` was written for the vertical
   Contact card, so its bottom rule has to become a vertical divider here. */
.opal-ty-contact{ flex-wrap:wrap; }
@media(min-width:768px){
  .opal-ty-contact > .opal-info-blk{
    flex:1 1 0;
    min-width:0;                       /* let long values wrap instead of pushing the row wide */
    padding:0 0 0 24px;
    border-bottom:0;
    border-left:1px solid var(--opal-line);
  }
  .opal-ty-contact > .opal-info-blk:first-child{
    padding-left:0;
    border-left:0;
  }
}
/* On mobile the shared compact-hero scrim starts at .6, and the eyebrow sits in
   exactly that lightest band — measured 4.14:1 against the photo, under AA for
   12.5px text. Steepened for this hero only so the Contact hero is untouched. */
@media(max-width:767px){
  .opal-hero--ty .opal-hero-media::after{
    background:linear-gradient(180deg,rgba(11,11,16,.82),rgba(11,11,16,.92));
  }
}

/* the email is longer than a third of a tablet row */
.opal-ty-contact .opal-info-val .elementor-heading-title{
  overflow-wrap:anywhere;
}
/* closing line sits apart from the contact blocks, above the section's own rule */
.opal-ty-close p{
  margin:0;
  font-size:1rem;
  line-height:1.6;
}


/* ==========================================================================
   G1 — Services mega-menu
   The live header keeps its nav-menu widget and its WP menu; the 5 new child
   pages were added as third-level menu items, so the Services panel simply
   becomes a grid. Nothing about the header template, the kit or the client's
   edits is touched — this is presentation only.
   ========================================================================== */

/* --- the panel itself: level-2 sub-menu under Services --- */
@media(min-width:1025px){
  .opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li.menu-item-43 > ul.sub-menu{
    width:min(940px, calc(100vw - 40px));
    min-width:0 !important;
    padding:22px;
    display:grid !important;
    grid-template-columns:1.15fr 1.15fr .95fr;
    gap:22px;
    border-radius:var(--opal-r-lg);
    box-shadow:0 24px 60px rgba(0,0,0,.5);
  }
  /* Below 1200 the wrap's gutter pins "Services" at x-centre 460, so a panel
     centred on it can only be 881px wide before its left edge leaves the
     viewport (measured: -10px at 1025). Capped rather than re-anchored, so the
     arrow keeps pointing at the link it belongs to. */
  @media(max-width:1199px){
    .opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li.menu-item-43 > ul.sub-menu{
      width:min(860px, calc(100vw - 40px));
    }
  }

  /* the little arrow tying the panel to the Services link */
  .opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li.menu-item-43 > ul.sub-menu::after{
    content:"";
    position:absolute;
    top:-8px;
    left:50%;
    transform:translateX(-50%) rotate(45deg);
    width:16px;
    height:16px;
    background:var(--opal-ink-2);
    border-left:1px solid var(--opal-line);
    border-top:1px solid var(--opal-line);
    border-radius:3px 0 0 0;
  }

  /* grid placement: Residential fills column 1, Commercial and New Construction
     stack in column 2, the featured card holds column 3 across both rows */
  .opal-hdr .opal-nav li.opal-mcol-1{ grid-column:1; grid-row:1 / span 2; }
  .opal-hdr .opal-nav li.opal-mcol-2{ grid-column:2; }
  .opal-hdr .opal-nav li.opal-mgap{ margin-top:16px; }
  .opal-hdr .opal-nav li.opal-mfeat{ grid-column:3; grid-row:1 / span 2; }

  /* --- pillar headings --- */
  .opal-hdr .opal-nav li.opal-mcol-1 > a.elementor-sub-item,
  .opal-hdr .opal-nav li.opal-mcol-2 > a.elementor-sub-item{
    display:flex !important;
    align-items:center;
    gap:10px;
    padding:0 8px 12px !important;
    margin-bottom:6px;
    border-bottom:1px solid var(--opal-line);
    border-radius:0 !important;
    font-size:.82rem !important;
    font-weight:600 !important;
    letter-spacing:.03em;
    text-transform:uppercase;
    color:var(--opal-peri) !important;
    background:none !important;
  }
  .opal-hdr .opal-nav li.opal-mcol-1 > a .elementor-item-desc,
  .opal-hdr .opal-nav li.opal-mcol-2 > a .elementor-item-desc{ display:none; }
  /* pillar heads are links, not disclosure triggers — the caret would lie */
  .opal-hdr .opal-nav li.opal-mcol-1 > a .sub-arrow,
  .opal-hdr .opal-nav li.opal-mcol-2 > a .sub-arrow{ display:none !important; }
  .opal-hdr .opal-nav li.opal-mcol-1 > a.elementor-sub-item::before,
  .opal-hdr .opal-nav li.opal-mcol-2 > a.elementor-sub-item::before{
    content:"";
    flex:0 0 auto;
    width:16px;
    height:16px;
    background-color:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M3%2010.5%2012%203l9%207.5%22%2F%3E%3Cpath%20d%3D%22M5%209.5V21h14V9.5%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M3%2010.5%2012%203l9%207.5%22%2F%3E%3Cpath%20d%3D%22M5%209.5V21h14V9.5%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  }
  .opal-hdr .opal-nav li.menu-item-45 > a.elementor-sub-item::before{
  -webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%2216%22%20height%3D%2218%22%20rx%3D%221%22%2F%3E%3Cpath%20d%3D%22M8%207h2M8%2011h2M8%2015h2M14%207h2M14%2011h2M14%2015h2%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%2216%22%20height%3D%2218%22%20rx%3D%221%22%2F%3E%3Cpath%20d%3D%22M8%207h2M8%2011h2M8%2015h2M14%207h2M14%2011h2M14%2015h2%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  }
  .opal-hdr .opal-nav li.menu-item-46 > a.elementor-sub-item::before{
  -webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M3%2021h18%22%2F%3E%3Cpath%20d%3D%22M6%2021V8l6-3%206%203v13%22%2F%3E%3Cpath%20d%3D%22M10%2021v-5h4v5%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M3%2021h18%22%2F%3E%3Cpath%20d%3D%22M6%2021V8l6-3%206%203v13%22%2F%3E%3Cpath%20d%3D%22M10%2021v-5h4v5%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  }

  /* --- the level-3 lists sit inside the panel; they are not dropdowns ---
     visibility/pointer-events are `inherit`, NOT `visible`/`auto`. They must
     follow the panel: forcing them on made the closed panel's links still
     hit-testable, so `li:hover` fired anywhere inside the panel's 940x316 box —
     the menu opened when the pointer was nowhere near "Services". opacity:0 on
     the panel hides pixels but does not stop hit-testing; visibility does, and
     only if a descendant does not override it. */
  .opal-hdr .opal-nav .elementor-nav-menu--main ul.sub-menu ul.sub-menu{
    position:static !important;
    transform:none !important;
    opacity:1 !important;
    visibility:inherit !important;
    pointer-events:inherit !important;
    width:auto !important;
    min-width:0 !important;
    padding:0 !important;
    margin:0 !important;
    background:none !important;
    border:0 !important;
    box-shadow:none !important;
    display:flex !important;
    flex-direction:column;
    gap:3px;
    transition:none !important;
  }
  .opal-hdr .opal-nav .elementor-nav-menu--main ul.sub-menu ul.sub-menu::before,
  .opal-hdr .opal-nav .elementor-nav-menu--main ul.sub-menu ul.sub-menu::after{ content:none !important; }

  /* --- child items: icon tile + title + descriptor --- */
  .opal-hdr .opal-nav li.opal-mi > a.elementor-sub-item{
    display:grid !important;
    grid-template-columns:34px 1fr;
    align-items:start;
    column-gap:12px;
    padding:9px 8px !important;
    border-radius:10px !important;
    background:none;
    transition:background .16s var(--opal-ease);
  }
  .opal-hdr .opal-nav li.opal-mi > a.elementor-sub-item:hover,
  .opal-hdr .opal-nav li.opal-mi > a.elementor-sub-item:focus-visible{
    background:rgba(185,158,249,.10) !important;
  }
  .opal-hdr .opal-nav li.opal-mi > a.elementor-sub-item::before{
    content:"";
    grid-row:1 / span 2;
    width:34px;
    height:34px;
    border-radius:9px;
    background-color:rgba(185,158,249,.14);
    display:block;
    transition:background-color .16s var(--opal-ease);
  }
  .opal-hdr .opal-nav li.opal-mi > a.elementor-sub-item::after{
    content:"";
    position:absolute;
    left:8px;
    top:9px;
    width:34px;
    height:34px;
    background-color:var(--opal-peri);
    transition:background-color .16s var(--opal-ease);
  }
  .opal-hdr .opal-nav li.opal-mi > a.elementor-sub-item{ position:relative; }
  .opal-hdr .opal-nav li.opal-mi > a:hover::before,
  .opal-hdr .opal-nav li.opal-mi > a:focus-visible::before{ background-color:var(--opal-peri); }
  .opal-hdr .opal-nav li.opal-mi > a:hover::after,
  .opal-hdr .opal-nav li.opal-mi > a:focus-visible::after{ background-color:var(--opal-ink); }
  .opal-hdr .opal-nav li.opal-mi > a .elementor-item-desc{
    grid-column:2;
    display:block;
    font-size:.76rem !important;
    font-weight:400 !important;
    color:var(--opal-on-dark-soft) !important;
    margin-top:2px;
    line-height:1.4;
    opacity:1;
  }
  .opal-hdr .opal-nav li.opal-mi > a.elementor-sub-item{
    font-size:.92rem !important;
    font-weight:500 !important;
    color:var(--opal-on-dark) !important;
    line-height:1.3;
  }

  /* per-item icons, masked so they take the tile's colour */
  .opal-hdr .opal-nav li.opal-mi-bathroom > a::after{
  -webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M4%2013h16v2a4%204%200%200%201-4%204H8a4%204%200%200%201-4-4z%22%2F%3E%3Cpath%20d%3D%22M6%2013V6a2%202%200%200%201%204%200%22%2F%3E%3Cline%20x1%3D%226%22%20y1%3D%2219%22%20x2%3D%225.5%22%20y2%3D%2221%22%2F%3E%3Cline%20x1%3D%2218%22%20y1%3D%2219%22%20x2%3D%2218.5%22%20y2%3D%2221%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M4%2013h16v2a4%204%200%200%201-4%204H8a4%204%200%200%201-4-4z%22%2F%3E%3Cpath%20d%3D%22M6%2013V6a2%202%200%200%201%204%200%22%2F%3E%3Cline%20x1%3D%226%22%20y1%3D%2219%22%20x2%3D%225.5%22%20y2%3D%2221%22%2F%3E%3Cline%20x1%3D%2218%22%20y1%3D%2219%22%20x2%3D%2218.5%22%20y2%3D%2221%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  }
  .opal-hdr .opal-nav li.opal-mi-shower > a::after{
  -webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M12%203s6%206%206%2010a6%206%200%200%201-12%200c0-4%206-10%206-10z%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M12%203s6%206%206%2010a6%206%200%200%201-12%200c0-4%206-10%206-10z%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  }
  .opal-hdr .opal-nav li.opal-mi-kitchen > a::after{
  -webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M6%203v6a2%202%200%200%200%204%200V3%22%2F%3E%3Cline%20x1%3D%228%22%20y1%3D%2211%22%20x2%3D%228%22%20y2%3D%2221%22%2F%3E%3Cpath%20d%3D%22M16%203c-1.4%200-2%202-2%204.5s.6%203.5%202%203.5%202-1%202-3.5S17.4%203%2016%203z%22%2F%3E%3Cline%20x1%3D%2216%22%20y1%3D%2211%22%20x2%3D%2216%22%20y2%3D%2221%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M6%203v6a2%202%200%200%200%204%200V3%22%2F%3E%3Cline%20x1%3D%228%22%20y1%3D%2211%22%20x2%3D%228%22%20y2%3D%2221%22%2F%3E%3Cpath%20d%3D%22M16%203c-1.4%200-2%202-2%204.5s.6%203.5%202%203.5%202-1%202-3.5S17.4%203%2016%203z%22%2F%3E%3Cline%20x1%3D%2216%22%20y1%3D%2211%22%20x2%3D%2216%22%20y2%3D%2221%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  }
  .opal-hdr .opal-nav li.opal-mi-window > a::after{
  -webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%2216%22%20height%3D%2218%22%20rx%3D%221%22%2F%3E%3Cline%20x1%3D%2212%22%20y1%3D%223%22%20x2%3D%2212%22%20y2%3D%2221%22%2F%3E%3Cline%20x1%3D%224%22%20y1%3D%2212%22%20x2%3D%2220%22%20y2%3D%2212%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%2216%22%20height%3D%2218%22%20rx%3D%221%22%2F%3E%3Cline%20x1%3D%2212%22%20y1%3D%223%22%20x2%3D%2212%22%20y2%3D%2221%22%2F%3E%3Cline%20x1%3D%224%22%20y1%3D%2212%22%20x2%3D%2220%22%20y2%3D%2212%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  }
  .opal-hdr .opal-nav li.opal-mi-fire > a::after{
  -webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M12%203c.5%202.5-1.5%204-2.5%205.5C8.5%2010%208%2011.2%208%2012.5a4%204%200%200%200%208%200c0-1.8-1-3.3-1.8-4.3C13%206.5%2012.4%204.8%2012%203z%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M12%203c.5%202.5-1.5%204-2.5%205.5C8.5%2010%208%2011.2%208%2012.5a4%204%200%200%200%208%200c0-1.8-1-3.3-1.8-4.3C13%206.5%2012.4%204.8%2012%203z%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
  }
  /* the mask fills the 34px tile; inset it so the glyph reads at 18px */
  .opal-hdr .opal-nav li.opal-mi > a.elementor-sub-item::after{
    -webkit-mask-size:18px 18px;
    mask-size:18px 18px;
  }

  /* --- New Construction note --- */
  .opal-hdr .opal-nav li.opal-mgap > a .elementor-item-desc{ display:none; }
  .opal-hdr .opal-nav li.opal-mgap::after{
    content:"Builder & new-development caulking and finishing across Melbourne.";
    display:block;
    font-size:.78rem;
    color:var(--opal-stone);
    padding:6px 8px 0;
    line-height:1.5;
  }

  /* --- featured card --- */
  .opal-hdr .opal-nav li.opal-mfeat > a.elementor-sub-item{
    position:relative;
    display:flex !important;
    flex-direction:column;
    justify-content:flex-end;
    height:100%;
    min-height:210px;
    padding:20px !important;
    border-radius:var(--opal-r-md) !important;
    overflow:hidden;
    /* The reference card points at bathroom-shower.webp, but that file is one of
       the two swapped uploads — it is actually exterior brickwork, and
       exterior-expansion-joint.webp holds the bathroom. Using the file that
       shows what the card means. Rename both at the source and update this. */
    background-image:url("/wp-content/uploads/2026/08/exterior-expansion-joint.webp");
    background-size:cover;
    background-position:center;
  }
  .opal-hdr .opal-nav li.opal-mfeat > a.elementor-sub-item::before{
    content:"";
    position:absolute;
    inset:0;
    background:linear-gradient(180deg,rgba(11,11,16,.15) 0%,rgba(11,11,16,.55) 45%,rgba(11,11,16,.92) 100%);
  }
  .opal-hdr .opal-nav li.opal-mfeat > a > *{ position:relative; z-index:1; }
  .opal-hdr .opal-nav li.opal-mfeat > a.elementor-sub-item{
    font-size:1.05rem !important;
    font-weight:500 !important;
    line-height:1.25;
    color:#fff !important;
  }
  .opal-hdr .opal-nav .opal-mfeat-tag{
    align-self:flex-start;
    margin-bottom:10px;
    padding:5px 12px;
    border-radius:var(--opal-r-pill);
    /* The translucent periwinkle chip measured 4.07:1 against the photo behind
       it — under AA, and it would shift with any future image. Near-opaque ink
       puts a floor under it: even over pure white the tag stays above 8:1. */
    background:rgba(11,11,16,.82);
    border:1px solid rgba(185,158,249,.55);
    color:var(--opal-lav);
    font-size:.72rem;
    font-weight:500;
    letter-spacing:.04em;
    text-transform:uppercase;
    line-height:1.4;
  }
  .opal-hdr .opal-nav li.opal-mfeat > a .elementor-item-desc{
    display:block;
    margin-top:6px;
    font-size:.82rem !important;
    font-weight:400 !important;
    line-height:1.5;
    color:var(--opal-on-dark-soft) !important;
  }
  .opal-hdr .opal-nav .opal-mfeat-cta{
    display:inline-flex;
    align-items:center;
    gap:6px;
    margin-top:14px;
    color:var(--opal-peri);
    font-size:.86rem;
    font-weight:600;
    transition:gap .16s var(--opal-ease);
  }
  .opal-hdr .opal-nav li.opal-mfeat > a:hover .opal-mfeat-cta{ gap:10px; }
}

/* --- chevron on the Services link (affordance that a panel opens) --- */
@media(min-width:1025px){
  .opal-hdr .opal-nav .elementor-nav-menu--main > ul.elementor-nav-menu > li.menu-item-43 > a.elementor-item{
    display:inline-flex !important;
    align-items:center;
    gap:6px;
  }
  .opal-hdr .opal-nav .elementor-nav-menu--main > ul > li.menu-item-43 > a .sub-arrow{
    display:inline-flex;
    align-items:center;
    transition:transform .22s var(--opal-ease);
    opacity:.85;
  }
  .opal-hdr .opal-nav .elementor-nav-menu--main > ul > li.menu-item-43:hover > a .sub-arrow,
  .opal-hdr .opal-nav .elementor-nav-menu--main > ul > li.menu-item-43:focus-within > a .sub-arrow,
  .opal-hdr .opal-nav .elementor-nav-menu--main > ul > li.menu-item-43.opal-sub-open > a .sub-arrow{
    transform:rotate(180deg);
    opacity:1;
  }
}

/* --- mobile / tablet: the children are indented links, no panel --- */
@media(max-width:1024px){
  /* second-level pillar rows keep the em dash they already had; the new third
     level is indented one step further so the hierarchy reads */
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown ul.sub-menu ul.sub-menu a.elementor-sub-item{
    padding-left:34px !important;
    font-size:.94rem !important;
    opacity:.72;
  }
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown ul.sub-menu ul.sub-menu a.elementor-sub-item::before{
    content:"— ";
  }
  /* the featured card is desktop chrome; the menu already ends in two CTAs */
  .opal-hdr .opal-nav .elementor-nav-menu--dropdown li.opal-mfeat{ display:none !important; }
}


/* ==========================================================================
   G2 — footer services structure
   The single "Services" column became two: Residential with its four children,
   and Commercial & New Build carrying Fire Rated plus the New Construction
   pillar. Everything else in the footer — the brand column, Explore, Contact,
   the bottom bar and the client's own credit line — is untouched.
   ========================================================================== */

/* pillar rows: on-dark, a touch heavier, with the pillar's own icon */
.opal-foot .opal-foot-pillars .elementor-icon-list-item > a{
  display:flex;
  align-items:center;
  gap:8px;
  padding:6px 0;
  color:var(--opal-on-dark) !important;
  font-weight:500;
  font-size:.92rem;
}
.opal-foot .opal-foot-pillars .elementor-icon-list-item > a:hover{
  color:var(--opal-peri) !important;
}
.opal-foot .opal-foot-pillars .elementor-icon-list-item > a::before{
  content:"";
  flex:0 0 auto;
  width:16px;
  height:16px;
  background-color:var(--opal-peri);
}
.opal-foot .opal-foot-pillar--res .elementor-icon-list-item > a::before{
    -webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M3%2010.5%2012%203l9%207.5%22%2F%3E%3Cpath%20d%3D%22M5%209.5V21h14V9.5%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
    mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M3%2010.5%2012%203l9%207.5%22%2F%3E%3Cpath%20d%3D%22M5%209.5V21h14V9.5%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
}
.opal-foot .opal-foot-pillar--com .elementor-icon-list-item > a::before{
    -webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%2216%22%20height%3D%2218%22%20rx%3D%221%22%2F%3E%3Cpath%20d%3D%22M8%207h2M8%2011h2M8%2015h2M14%207h2M14%2011h2M14%2015h2%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
    mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%2216%22%20height%3D%2218%22%20rx%3D%221%22%2F%3E%3Cpath%20d%3D%22M8%207h2M8%2011h2M8%2015h2M14%207h2M14%2011h2M14%2015h2%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
}
.opal-foot .opal-foot-pillar--nc .elementor-icon-list-item > a::before{
    -webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M3%2021h18%22%2F%3E%3Cpath%20d%3D%22M6%2021V8l6-3%206%203v13%22%2F%3E%3Cpath%20d%3D%22M10%2021v-5h4v5%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
    mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M3%2021h18%22%2F%3E%3Cpath%20d%3D%22M6%2021V8l6-3%206%203v13%22%2F%3E%3Cpath%20d%3D%22M10%2021v-5h4v5%22%2F%3E%3C%2Fsvg%3E") center/contain no-repeat;
}
/* New Construction sits under the Fire Rated child, so it needs air above it */
.opal-foot .opal-foot-pillar--gap{ margin-top:14px; }

/* child rows: indented, quieter, with a periwinkle dot */
.opal-foot .opal-foot-children .elementor-icon-list-item > a{
  position:relative;
  display:block;
  padding:4px 0 4px 24px;
  font-size:.86rem;
  opacity:.82;
}
.opal-foot .opal-foot-children .elementor-icon-list-item > a::before{
  content:"";
  position:absolute;
  left:11px;
  top:.95em;
  width:5px;
  height:5px;
  border-radius:50%;
  background:var(--opal-peri);
  opacity:.6;
}
.opal-foot .opal-foot-children .elementor-icon-list-item > a:hover{
  opacity:1;
  color:var(--opal-peri) !important;
}
.opal-foot .opal-foot-children .elementor-icon-list-item > a:hover::before{ opacity:1; }

/* Elementor puts the list's typography on the inner .elementor-icon-list-text
   span, so size and weight have to be set there — on the <a> they lose. */
.opal-foot .opal-foot-pillars .elementor-icon-list-text{
  font-size:.92rem !important;
  font-weight:500 !important;
  color:var(--opal-on-dark) !important;
}
.opal-foot .opal-foot-pillars .elementor-icon-list-item > a:hover .elementor-icon-list-text{
  color:var(--opal-peri) !important;
}
.opal-foot .opal-foot-children .elementor-icon-list-text{
  font-size:.86rem !important;
  font-weight:400 !important;
  color:var(--opal-on-dark-soft) !important;
}
.opal-foot .opal-foot-children .elementor-icon-list-item > a:hover .elementor-icon-list-text{
  color:var(--opal-peri) !important;
}

/* the two service columns run tighter than the default list spacing */
.opal-foot .opal-foot-pillars .elementor-icon-list-items,
.opal-foot .opal-foot-children .elementor-icon-list-items{ gap:0; }
.opal-foot .opal-foot-pillars .elementor-icon-list-icon,
.opal-foot .opal-foot-children .elementor-icon-list-icon{ display:none !important; }

@media(max-width:980px){
  /* four content columns wrap to two; the brand column keeps its own row */
  .opal-foot-cols > .e-con-inner{ grid-template-columns:1fr 1fr; }
}
@media(max-width:767px){
  .opal-foot-cols > .e-con-inner{ grid-template-columns:1fr; }
}

/* ==========================================================================
   BA-01 — hero breadcrumb (reference .hero .crumb)
   The only new component in the single-service hero; everything else reuses
   .opal-hero / .opal-hero--sub / .opal-hero-h1 / .opal-hero-lead / .opal-hero-cta
   exactly as the pillar heroes do.
   ========================================================================== */
.opal-crumb .opal-crumb-nav ol{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  /* 10px here + 10px on the li puts 24px between adjacent links, which is what
     WCAG 2.2 SC 2.5.8 asks for as target offset (QA phase 7). */
  gap:10px;
  margin:0;
  padding:0;
  list-style:none;
  font-size:.82rem;
  line-height:1.4;
  color:var(--opal-on-dark-soft);
}
.opal-crumb .opal-crumb-nav li{ display:flex; align-items:center; gap:10px; }
/* the separator is decorative, so it is drawn rather than written — a screen
   reader hears the list, not a run of chevrons */
.opal-crumb .opal-crumb-nav li + li::before{
  content:"›";
  opacity:.5;
  speak:never;
}
.opal-crumb .opal-crumb-nav a,
.opal-crumb .opal-crumb-nav [aria-current="page"]{
  /* 13.12px/1.4 text is only 18px tall, under the 24px minimum target size in
     WCAG 2.2 SC 2.5.8. 3px of block padding lifts the hit area to 24px without
     moving the text (QA phase 7). */
  display:inline-block;
  padding-block:3px;
}
.opal-crumb .opal-crumb-nav a{
  color:var(--opal-on-dark-soft);
  text-decoration:none;
}
.opal-crumb .opal-crumb-nav a:hover{ color:var(--opal-peri); }
.opal-crumb .opal-crumb-nav [aria-current="page"]{ color:var(--opal-on-dark); }
/* The reference spaces this with .crumb{margin-bottom:16px}, but Elementor
   zeroes widget margins inside a flex container, so the 16px is set on the H1
   through Elementor's own margin control — the same way the lead and CTA row
   space themselves on every other hero. */

/* ==========================================================================
   BA-02 — metrics row (reference .metrics / .metric)
   Four client-stated facts under the intro split. The split itself reuses
   .opal-split / .opal-split-media exactly as the pillar pages do.
   ========================================================================== */
.opal-metrics > .e-con-inner{
  display:grid !important;
  grid-template-columns:repeat(4,1fr);
  gap:20px;
  align-items:stretch;
}
.opal-metrics > .e-con-inner > .e-con{
  width:100% !important;
  max-width:100% !important;
}
.opal-metric{
  padding:22px 24px;
  border-radius:var(--opal-r-md);
  background:var(--opal-white);
  border:1px solid #ECE9F4;
}
.opal-metric-num .elementor-heading-title{
  display:block;
  font-size:1.9rem;
  font-weight:700;
  line-height:1;
  letter-spacing:-.01em;
}
.opal-metric-lbl p{
  margin:0;
  font-size:.86rem;
  line-height:1.5;
}
/* the numbers vary in length — "6+" against "Res + Com" — so the labels are
   pinned to the bottom and every card in the row keeps one baseline */
.opal-metric > .e-con-inner{
  display:flex;
  flex-direction:column;
  height:100%;
}
.opal-metric .opal-metric-lbl{ margin-top:auto; }

@media(max-width:980px){
  .opal-metrics > .e-con-inner{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:520px){
  .opal-metrics > .e-con-inner{ grid-template-columns:1fr; }
}


/* ==========================================================================
   BA-03 — signs grid (reference .signs / .sign)
   Five warning cards plus one emphasis card that answers them. The icon is a
   fixed decorative glyph, so it is drawn on the card's ::before rather than
   carried as a widget — one element instead of two per card, and the copy stays
   the only editable thing in each.
   ========================================================================== */
.opal-signs > .e-con-inner{
  display:grid !important;
  grid-template-columns:repeat(3,1fr);
  gap:18px;
  align-items:stretch;
}
.opal-signs > .e-con-inner > .e-con{
  width:100% !important;
  max-width:100% !important;
}

.opal-sign{
  position:relative;
  padding:22px !important;
  border-radius:var(--opal-r-md);
  background:var(--opal-white);
  border:1px solid #ECE9F4;
  border-left:3px solid var(--opal-peri);
  transition:transform .2s var(--opal-ease), box-shadow .2s var(--opal-ease);
}
.opal-sign:hover{
  transform:translateY(-3px);
  box-shadow:var(--opal-sh);
}
/* The tile lives on the text widget, not on the card. Elementor already owns
   `.e-con::before` for its background-overlay layer and forces it
   position:absolute, so a tile drawn there sits on top of the copy instead of
   beside it — which is exactly what happened first time round. */
.opal-sign .opal-sign-txt{
  display:flex;
  flex-direction:row;
  align-items:flex-start;
  gap:14px;
  width:100%;
}
.opal-sign .opal-sign-txt::before{
  content:"";
  flex:0 0 auto;
  width:38px;
  height:38px;
  border-radius:10px;
  background-color:rgba(185,158,249,.16);
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%239B7EE0%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%2F%3E%3Cline%20x1%3D%2212%22%20y1%3D%228%22%20x2%3D%2212%22%20y2%3D%2212%22%2F%3E%3Cline%20x1%3D%2212%22%20y1%3D%2216%22%20x2%3D%2212.01%22%20y2%3D%2216%22%2F%3E%3C%2Fsvg%3E");
  background-repeat:no-repeat;
  background-position:center;
  background-size:20px 20px;
}
.opal-sign--em .opal-sign-txt::before{
  background-color:var(--opal-peri);
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%230B0B10%22%20stroke-width%3D%222.4%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpolyline%20points%3D%2220%206%209%2017%204%2012%22%2F%3E%3C%2Fsvg%3E");
}
.opal-sign .opal-sign-txt p{
  margin:0;
  font-size:.95rem;
  font-weight:500;
  line-height:1.4;
}

/* the emphasis card answers the five warnings, so it inverts */
.opal-sign--em{
  background:var(--opal-ink);
  border-color:var(--opal-line);
  border-left-color:var(--opal-peri-d);
}
.opal-sign--em::before{
  background-color:var(--opal-peri);
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%230B0B10%22%20stroke-width%3D%222.4%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpolyline%20points%3D%2220%206%209%2017%204%2012%22%2F%3E%3C%2Fsvg%3E");
}

@media(max-width:980px){
  .opal-signs > .e-con-inner{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:600px){
  .opal-signs > .e-con-inner{ grid-template-columns:1fr; }
}

/* fr-03 lists SEVEN applications, which strands a lone tile on a fixed
   3-column grid. The reference's own .sgrid uses auto-fill rather than a fixed
   count, so this modifier mirrors that: tiles size themselves and 7 items land
   4 + 3 at desktop instead of 3 + 3 + 1. */
.opal-signs--auto > .e-con-inner{
  grid-template-columns:repeat(auto-fill,minmax(215px,1fr));
  gap:16px;
}
@media(max-width:980px){
  .opal-signs--auto > .e-con-inner{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:600px){
  .opal-signs--auto > .e-con-inner{ grid-template-columns:1fr; }
}
@media(prefers-reduced-motion:reduce){
  .opal-sign{ transition:none; }
  .opal-sign:hover{ transform:none; }
}

/* ==========================================================================
   FR-03 — fire-rated joint-type diagrams
   Explanatory line art, NOT photography of completed work. Fire-rated jobsite
   photos may not be invented (see the FR-07 placeholder note), so where this
   page would otherwise be visually thin it carries schematics instead. They are
   deliberately generic: no rating, no tested system, no product, no approval
   mark — the caption says so, and the section's closing paragraph repeats that
   each project follows its own specified building system and documentation.

   Inline SVG rather than an image file: crisp at any size, no extra request,
   recolours with the tokens, and readable when a stylesheet fails to load.
   ========================================================================== */
.opal-fdiag > .e-con-inner,
.opal-fdiag{ margin-top:44px; }
.opal-fdiag-grid{
  display:grid;
  grid-template-columns:repeat(3,1fr);
  gap:18px;
  margin:0;
  padding:0;
  list-style:none;
}
.opal-fdiag-fig{
  margin:0;
  background:var(--opal-white);
  border:1px solid #ECE9F4;
  border-radius:var(--opal-r-md);
  padding:18px 18px 16px;
}
.opal-fdiag-fig svg{ display:block; width:100%; height:auto; }
.opal-fdiag-fig figcaption{ margin-top:12px; }
.opal-fdiag-fig b{
  display:block;
  font-family:var(--opal-font, Poppins, sans-serif);
  font-weight:700;
  font-size:.95rem;
  line-height:1.3;
  color:var(--opal-body-ink, #23232B);
}
.opal-fdiag-fig span{
  display:block;
  margin-top:4px;
  font-size:.85rem;
  line-height:1.5;
  color:var(--opal-body-ink, #23232B);
}
/* the drawing's own parts */
.opal-fdiag-sub{ fill:#EFEDF6; stroke:#23232B; stroke-width:1.5; }
.opal-fdiag-line{ fill:none; stroke:#23232B; stroke-width:1.5; }
.opal-fdiag-seal{ fill:#7850D5; }          /* 4.52 on this section's mist, 3:1+ as a graphic */
.opal-fdiag-rod{ fill:#FFFFFF; stroke:#23232B; stroke-width:1.5; }
.opal-fdiag-key{
  margin-top:18px;
  display:flex;
  flex-wrap:wrap;
  gap:8px 22px;
  align-items:center;
  font-size:.85rem;
  color:var(--opal-body-ink, #23232B);
}
.opal-fdiag-key i{
  display:inline-block; width:14px; height:14px; border-radius:3px;
  margin-right:8px; vertical-align:-2px;
}
.opal-fdiag-note{
  margin-top:14px;
  font-size:.85rem;
  line-height:1.6;
  /* Body Ink, not Stone: Stone on this section's mist measures 4.47, the same
     near-miss already corrected elsewhere in the build. This note carries the
     regulatory qualifier, so it is the last thing that should be hard to read. */
  color:var(--opal-body-ink, #23232B);
}
@media(max-width:980px){ .opal-fdiag-grid{ grid-template-columns:repeat(2,1fr); } }
@media(max-width:600px){ .opal-fdiag-grid{ grid-template-columns:1fr; } }

/* ==========================================================================
   FR-04 — context band under the process steps
   Fire Rated carries no photograph of fire-rated work and none may be invented,
   which left ~2,000px of this page with nothing visual. This band is general
   construction CONTEXT — a bare building under construction, with no sealant,
   joint or penetration anywhere in frame — so it cannot be read as depicting the
   process described above it. Its media-library title and the widget title both
   record that it is illustrative and not a job photo.
   ========================================================================== */
.opal-frband{ margin-top:48px !important; }
.opal-frband .elementor-widget-container,
.opal-frband figure{ margin:0; }
.opal-frband img{
  display:block;
  width:100%;
  height:clamp(190px, 24vw, 320px);
  object-fit:cover;
  border-radius:var(--opal-r-lg);
}

/* ==========================================================================
   FR-07 — truth-guardrail placeholder note (reference .ph-note)
   Fire Rated is a regulated service and no stock or generated "fire-rated"
   photograph may stand in for real work. Where the reference would otherwise
   have shown jobsite imagery it shows this marked note instead — deliberately
   visible, dashed and tinted, so it reads as an open item rather than as
   finished copy. Remove it only when the client supplies genuine photographs.
   ========================================================================== */
.opal-ph-note{
  margin-top:28px !important;
  border:1px dashed var(--opal-peri-d, #9B7EE0);
  background:rgba(185,158,249,.08);
  border-radius:var(--opal-r-md);
  padding:16px 20px;
  font-size:.9rem;
  line-height:1.6;
  color:var(--opal-stone);
}
.opal-ph-note b{ color:var(--opal-ink); font-weight:700; letter-spacing:.02em; }
.opal-ph-note a{ color:var(--opal-peri-ink); font-weight:500; }  /* peri-ink, not
   peri-d: the reference styles this link #9B7EE0, which measures 3.25 on light and
   fails AA for .9rem text — the same accent-on-light problem already fixed sitewide. */
.opal-ondark .opal-ph-note{
  color:var(--opal-on-dark-soft, #C9C6D6);
  border-color:var(--opal-peri);
}
.opal-ondark .opal-ph-note b{ color:var(--opal-on-dark); }
.opal-ondark .opal-ph-note a{ color:var(--opal-peri); }

/* ==========================================================================
   FR-04 — horizontal numbered steps (reference .steps / .st)
   Fire Rated is the only page that uses this variant: there is no photograph of
   fire-rated work and none may be invented, so the reference drops the media
   panel and runs four steps across instead of the photo+column layout the other
   pages use.

   The number is a CSS counter, like .opal-stepsv, so reordering or inserting a
   step renumbers on its own. It is drawn on the H3 WIDGET's ::before as a static
   block — not on the step container's — because Elementor owns `.e-con::before`
   for its background-overlay layer and forces position:absolute there (BA-03).
   `counter-increment` sits on the container and the counter is read from the
   heading, which works because CSS counters inherit down the tree.
   ========================================================================== */
.opal-steps > .e-con-inner{
  display:grid !important;
  grid-template-columns:repeat(4,1fr);
  gap:24px;
  align-items:start;
  counter-reset:opal-st;
}
.opal-steps > .e-con-inner > .e-con{
  width:100% !important;
  max-width:100% !important;
}
.opal-st{
  counter-increment:opal-st;
  padding:22px 0 0 0 !important;
  border-top:2px solid var(--opal-line);
}
.opal-st .opal-st-h3::before{
  content:"0" counter(opal-st);
  display:block;
  font-weight:700;
  font-size:1.5rem;
  line-height:1.1;
  color:var(--opal-peri);
  margin-bottom:10px;
}
@media(max-width:980px){
  .opal-steps > .e-con-inner{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:600px){
  .opal-steps > .e-con-inner{ grid-template-columns:1fr; }
}

/* ==========================================================================
   BA-04 — process: photo + numbered vertical steps
   (reference .procwrap / .stepsv / .sv)
   The step numbers are CSS counters rather than typed digits, so reordering or
   inserting a step renumbers the list on its own. Per BA-03: the tile is drawn
   on a widget's ::before, never on an Elementor container's.
   ========================================================================== */
.opal-procwrap > .e-con-inner{
  display:grid !important;
  grid-template-columns:.85fr 1.15fr;
  gap:clamp(30px,5vw,60px);
  align-items:center;
}
.opal-procwrap > .e-con-inner > .e-con{
  width:100% !important;
  max-width:100% !important;
}

/* the photo, with its caption badge pinned into the corner */
.opal-procmedia{
  position:relative;
  border-radius:var(--opal-r-lg);
  overflow:hidden;
  aspect-ratio:3/4;
  box-shadow:var(--opal-sh);
}
.opal-procmedia .elementor-widget-image,
.opal-procmedia .elementor-widget-image .elementor-widget-container,
.opal-procmedia figure{ height:100%; width:100%; margin:0; }
.opal-procmedia img{
  width:100% !important;
  height:100% !important;
  object-fit:cover;
  display:block;
}
.opal-procbadge{
  position:absolute;
  left:16px;
  bottom:16px;
  z-index:1;
  width:auto !important;
}
.opal-procbadge span{
  display:inline-block;
  padding:8px 14px;
  border-radius:var(--opal-r-pill);
  background:rgba(11,11,16,.72);
  backdrop-filter:blur(6px);
  border:1px solid rgba(255,255,255,.14);
  color:var(--opal-on-dark);
  font-size:.78rem;
  font-weight:500;
  line-height:1.4;
}

/* the numbered list */
.opal-stepsv{ counter-reset:opal-sv; }
.opal-sv{
  counter-increment:opal-sv;
  position:relative;
  padding:18px 0 18px 64px !important;
  border-bottom:1px solid var(--opal-line);
}
.opal-sv:last-child{ border-bottom:0; }
/* Elementor gives every widget position:relative, which would make the H3 the
   containing block and drop the tile on top of the step's body copy. Static
   returns the row to being the offset parent. */
.opal-sv .opal-sv-h3{ position:static; }
.opal-sv .opal-sv-h3::before{
  content:counter(opal-sv);
  position:absolute;
  left:0;
  top:18px;
  width:46px;
  height:46px;
  border-radius:12px;
  display:grid;
  place-items:center;
  background:rgba(185,158,249,.16);
  color:var(--opal-peri);
  font-weight:700;
  font-size:1.05rem;
  line-height:1;
}
.opal-sv .opal-sv-p p{ margin:0; }

@media(max-width:980px){
  .opal-procwrap > .e-con-inner{ grid-template-columns:1fr; }
  /* the photo leads on one column, as in the reference */
  .opal-procmedia{ order:-1; aspect-ratio:16/10; }
}

/* ==========================================================================
   BA-05 — material / tile-style cards (reference .mats / .mat)
   Two photo cards and one icon card. The icon card exists because the library
   has no mosaic photograph — an illustrated tile grid is honest where a stand-in
   photograph would not be.
   ========================================================================== */
.opal-mats > .e-con-inner{
  display:grid !important;
  grid-template-columns:repeat(3,1fr);
  gap:22px;
  align-items:stretch;
}
.opal-mats > .e-con-inner > .e-con{
  width:100% !important;
  max-width:100% !important;
}

.opal-mat{
  display:flex !important;
  flex-direction:column !important;
  background:var(--opal-white);
  border:1px solid #ECE9F4;
  border-radius:var(--opal-r-lg);
  overflow:hidden;
  transition:transform .25s var(--opal-ease), box-shadow .25s var(--opal-ease);
}
.opal-mat:hover{
  transform:translateY(-4px);
  box-shadow:var(--opal-sh);
}

/* photo top */
.opal-mat-ph{
  aspect-ratio:16/11;
  overflow:hidden;
  background:var(--opal-ink-2);
}
.opal-mat-ph .elementor-widget-image,
.opal-mat-ph .elementor-widget-image .elementor-widget-container,
.opal-mat-ph figure{ height:100%; width:100%; margin:0; }
.opal-mat-ph img{
  width:100% !important;
  height:100% !important;
  object-fit:cover;
  display:block;
  transition:transform .5s var(--opal-ease);
}
.opal-mat:hover .opal-mat-ph img{ transform:scale(1.05); }

/* icon top */
.opal-mat-ic{
  aspect-ratio:16/9;
  display:grid !important;
  place-items:center;
  background:linear-gradient(120deg,#171326,#0B0B10);
}
.opal-mat-ic .opal-mat-ic-svg{ width:auto !important; line-height:0; }
.opal-mat-ic svg{ color:var(--opal-peri); display:block; }

/* card body */
.opal-mat-b{ padding:24px !important; }
.opal-mat-p p{ margin:0; }

@media(max-width:980px){
  .opal-mats > .e-con-inner{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:600px){
  .opal-mats > .e-con-inner{ grid-template-columns:1fr; }
}
@media(prefers-reduced-motion:reduce){
  .opal-mat,
  .opal-mat-ph img{ transition:none; }
  .opal-mat:hover{ transform:none; }
  .opal-mat:hover .opal-mat-ph img{ transform:none; }
}

/* ==========================================================================
   BA-07 — "we assist with" pills (reference .assist)
   An Elementor icon-list rendered as a wrapping pill row, so the markup stays a
   real <ul>/<li> while reading as chips.
   ========================================================================== */
.opal-assist .elementor-icon-list-items{
  display:flex !important;
  flex-wrap:wrap;
  gap:10px;
  margin:0;
  padding:0;
}
.opal-assist .elementor-icon-list-item{
  display:inline-flex !important;
  align-items:center;
  gap:8px;
  margin:0 !important;
  /* Elementor's icon-list `space_between` writes its own block padding on each
     item (half the gap top and bottom), which clipped the pill to 9/16/5.
     The row gap is handled by flex `gap` here, so the item padding is fixed. */
  padding:9px 16px !important;
  border-radius:var(--opal-r-pill);
  background:var(--opal-mist);
  color:#41414D;
  font-size:.9rem;
  font-weight:500;
  line-height:1.4;
}
.opal-assist .elementor-icon-list-item > a,
.opal-assist .elementor-icon-list-item > span{
  display:inline-flex;
  align-items:center;
  gap:8px;
  padding:0;
}
.opal-assist .elementor-icon-list-text{
  color:inherit !important;
  padding:0 !important;
}
/* Same trap as .opal-ticks: Elementor's icon_color control writes `fill`, but
   these are stroke icons drawn with currentColor — the colour has to come from
   `color` or the tick inherits the pill's text colour. */
.opal-assist .elementor-icon-list-icon{
  flex:0 0 auto;
  display:flex;
  color:var(--opal-peri-ink);
}
.opal-assist .elementor-icon-list-icon svg{
  width:15px;
  height:15px;
  fill:none;
  stroke:currentColor;
}

/* ==========================================================================
   BA-08 — why-choose checklist on dark (reference .whylist)
   Two columns of ticked statements. Same icon-list-as-layout approach as the
   assist pills, so the markup stays a real <ul>.
   ========================================================================== */
.opal-whylist .elementor-icon-list-items{
  display:grid !important;
  grid-template-columns:1fr 1fr;
  gap:16px 32px;
  margin:0;
  padding:0;
}
.opal-whylist .elementor-icon-list-item{
  display:flex !important;
  align-items:flex-start;
  gap:12px;
  /* Elementor's space_between writes block padding on each item; the grid gap
     already spaces this list, so the item padding is cleared. */
  padding:0 !important;
  margin:0 !important;
  font-size:1rem;
  line-height:1.5;
}
.opal-whylist .elementor-icon-list-item > a,
.opal-whylist .elementor-icon-list-item > span{
  display:flex;
  align-items:flex-start;
  gap:12px;
  padding:0;
}
.opal-whylist .elementor-icon-list-text{
  color:var(--opal-on-dark) !important;
  padding:0 !important;
}
/* stroke icon — colour comes from `color`, not Elementor's fill-based control */
.opal-whylist .elementor-icon-list-icon{
  flex:0 0 auto;
  display:flex;
  margin-top:3px;
  color:var(--opal-peri);
}
.opal-whylist .elementor-icon-list-icon svg{
  width:20px;
  height:20px;
  fill:none;
  stroke:currentColor;
}

@media(max-width:767px){
  .opal-whylist .elementor-icon-list-items{ grid-template-columns:1fr; }
}

/* ==========================================================================
   Single-service hero scrim
   The shared sub-hero mobile ramp (.6 → .88) was tuned against the pillar
   photographs. The single-service pages use brighter images — the subway-tile
   shower measured crumb 4.78 and the accent word 4.01, both passing but with no
   headroom, and the white kitchen splashback still to come is brighter again.
   A steeper ramp is scoped to these pages so the live pillar heroes keep the
   scrim they were signed off with.
   ========================================================================== */
@media(max-width:767px){
  .opal-hero--ss .opal-hero-media::after{
    background:linear-gradient(180deg, rgba(11,11,16,.74), rgba(11,11,16,.92));
  }
}

/* SH-03 — the shower page closes its signs section with prose instead of the
   dark emphasis card Bathroom uses; the reference caps that line at 70ch. */
.opal-signs-close p{
  max-width:70ch;
  margin:0;
}
