{"id":73,"date":"2026-08-19T18:29:45","date_gmt":"2026-08-19T18:29:45","guid":{"rendered":"https:\/\/onexmechanical.ca\/?page_id=73"},"modified":"2026-08-19T18:34:06","modified_gmt":"2026-08-19T18:34:06","slug":"home","status":"publish","type":"page","link":"https:\/\/onexmechanical.ca\/","title":{"rendered":"HOME"},"content":{"rendered":"\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n     ONEX PLUMBING \u2014 HOMEPAGE, PASTE-READY FOR WORDPRESS\n\n     This is the same page, with <!DOCTYPE>, <html>, <head> and <body>\n     removed, because WordPress supplies those itself. Paste ALL of it\n     into a single Elementor \"HTML\" widget on a Canvas-template page.\n\n     The <title> and meta description are NOT here on purpose \u2014 set\n     those in your SEO plugin or the page settings instead, or you end\n     up with two of each and Google ignores both.\n\n     \u26a0 TWO THINGS STILL TO DO:\n     1. The photo below points at \"onex-sink-drain-repair.jpg\". Upload\n        it to Media Library, copy the URL WordPress gives you, and\n        paste that URL over the filename.\n     2. The form action is still the YOUR_FORM_ID placeholder.\n     \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Instrument+Sans:wght@500;600;700&#038;family=Inter:wght@400;500;600;700;800&#038;display=swap\" rel=\"stylesheet\">\n\n<style>\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   DESIGN SYSTEM\n   Matte black is a surface, not a hex code: warm-\n   tinted blacks, low specular contrast, soft light\n   falloff, and a fine grain over everything.\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n:root{\n  \/* Surfaces \u2014 all carry a slight warm tint so the black reads matte, not screen-glare *\/\n  --void:     #0D0D0D;\n  --matte:    #151515;\n  --raise:    #1C1C1C;\n  --edge:     rgba(246,246,244,.13);\n  --edge-hi:  rgba(246,246,244,.30);\n\n  \/* Ink *\/\n  --chalk:    #F6F6F4;\n  --chalk-70: rgba(246,246,244,.90);\n  --chalk-45: rgba(246,246,244,.74);\n  --chalk-28: rgba(246,246,244,.60);\n\n  \/* Accents are monochrome \u2014 the site is black and white. These stay as\n     named tokens so every rule below keeps working; only the values changed.\n     Emphasis now comes from brightness and weight, not hue. *\/\n  --brass:    rgba(246,246,244,.55);\n  --brass-hi: #F6F6F4;\n  --spring:   cubic-bezier(.34,1.56,.64,1);\n\n  --ember:    rgba(246,246,244,.55);\n  --ember-hi: #F6F6F4;\n\n  --display: 'Instrument Sans', system-ui, sans-serif;\n  --body:    'Inter', system-ui, sans-serif;\n\n  --t-hero:  clamp(2.35rem, 7vw, 5.6rem);\n  --t-h2:    clamp(1.9rem, 4vw, 3.4rem);\n  --t-h3:    clamp(1.15rem, 1.7vw, 1.45rem);\n  --t-lede:  clamp(1rem, 1.35vw, 1.2rem);\n  --t-sm:    1rem;\n  --t-xs:    .82rem;\n\n  --r-lg: 22px;\n  --r-md: 16px;\n  --r-pill: 999px;\n\n  --s1:8px; --s2:16px; --s3:24px; --s4:36px;\n  --s5:52px; --s6:76px; --s7:108px; --s8:150px;\n\n  --gutter: clamp(20px, 5vw, 76px);\n  --maxw: 1400px;\n\n  --ease: cubic-bezier(.22,1,.36,1);\n}\n\n*,*::before,*::after{ margin:0; padding:0; box-sizing:border-box; }\nhtml{ scroll-behavior:smooth; -webkit-text-size-adjust:100%; background:#0D0D0D; color-scheme:dark; }\nbody{\n  background:var(--void); color:var(--chalk);\n  font-family:var(--body); font-size:1.06rem; line-height:1.68;\n  font-weight:400; letter-spacing:-.005em;\n  -webkit-font-smoothing:antialiased; overflow-x:hidden;\n}\na{ color:inherit; text-decoration:none; }\nimg{ max-width:100%; display:block; }\n:focus-visible{ outline:2px solid var(--chalk); outline-offset:4px; border-radius:4px; }\n\n\n.wrap{ max-width:var(--maxw); margin-inline:auto; padding-inline:var(--gutter); }\n.band{ padding-block:clamp(var(--s6), 9vw, var(--s8)); position:relative; scroll-margin-top:90px; }\n.band--matte{ background:var(--matte); }\n\n.eyebrow{\n  display:inline-flex; align-items:center; gap:10px;\n  font-size:var(--t-xs); font-weight:600; letter-spacing:.14em;\n  text-transform:uppercase; color:var(--chalk-45);\n}\n.eyebrow::before{ content:''; width:26px; height:1px; background:var(--chalk-28); }\n\nh1,h2,h3{ font-family:var(--display); font-weight:700; letter-spacing:-.03em; line-height:1.04; }\n.h2{ font-size:var(--t-h2); margin-top:var(--s3); max-width:16ch; }\n.lede{ color:var(--chalk-70); font-size:var(--t-lede); max-width:56ch; margin-top:var(--s3); }\n\n.btn{\n  display:inline-flex; align-items:center; justify-content:center; gap:10px;\n  font-family:var(--body); font-size:var(--t-sm); font-weight:700; letter-spacing:-.01em;\n  padding:17px 30px; border-radius:var(--r-pill); border:1px solid transparent;\n  transition:transform .35s var(--ease), background .3s var(--ease), color .3s var(--ease), border-color .3s var(--ease);\n}\n.btn:hover{ transform:translateY(-2px); }\n.btn--light{ background:var(--chalk); color:var(--void); }\n.btn--light:hover{ background:#fff; }\n.btn--line{ border-color:var(--edge-hi); color:var(--chalk); }\n.btn--line:hover{ border-color:var(--chalk); background:rgba(246,246,244,.06); }\n\n\/* \u2500\u2500 Nav \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n.nav{\n  position:sticky; top:0; z-index:100;\n  background:#0D0D0D;\n  border-bottom:1px solid var(--edge);\n}\n.nav__in{ display:flex; align-items:center; justify-content:space-between; gap:var(--s3); height:74px; }\n.logo{ font-family:var(--display); font-weight:700; font-size:1.05rem; letter-spacing:-.02em;\n  white-space:nowrap; display:inline-flex; align-items:baseline; gap:7px; }\n.logo em{ font-style:normal; color:var(--chalk-45); font-weight:500; font-size:.82em; }\n.logo:hover{ animation:wobble .5s var(--spring); }\n@keyframes wobble{ 30%{ transform:rotate(-2.5deg) scale(1.03);} 60%{ transform:rotate(1.5deg);} 100%{ transform:none;} }\n\n.nav__links{ display:flex; align-items:center; gap:var(--s3); list-style:none; }\n.nav__links > li{ position:relative; }\n.nav__links a,\n.nav__links .sub__btn{\n  position:relative; display:inline-flex; align-items:center; gap:6px;\n  font-family:var(--body); font-size:var(--t-sm); font-weight:500;\n  color:var(--chalk-70); background:none; border:0; cursor:pointer;\n  transition:color .25s, transform .35s var(--spring);\n}\n.nav__links > li > a:hover,\n.nav__links .sub__btn:hover{ color:var(--chalk); transform:translateY(-3px) rotate(-1.2deg); }\n\/* a bead of brass rolls in under the label *\/\n.nav__links > li > a::after,\n.nav__links .sub__btn::after{\n  content:''; position:absolute; left:50%; bottom:-9px; width:6px; height:6px; margin-left:-3px;\n  border-radius:50%; background:var(--brass-hi);\n  opacity:0; transform:translateY(-6px) scale(.4);\n  transition:opacity .25s, transform .4s var(--spring);\n}\n.nav__links > li > a:hover::after,\n.nav__links .sub__btn:hover::after,\n.has-sub:focus-within .sub__btn::after{ opacity:1; transform:translateY(0) scale(1); }\n.nav__links .is-ember{ color:var(--ember-hi); font-weight:600; }\n.nav__links .is-ember:hover{ color:#fff; }\n.chev{ width:9px; height:9px; border-right:1.5px solid currentColor; border-bottom:1.5px solid currentColor;\n  transform:rotate(45deg) translateY(-2px); transition:transform .4s var(--spring); }\n\n\/* Desktop dropdown *\/\n.sub{\n  position:absolute; top:100%; left:-18px; min-width:272px;\n  list-style:none; padding:10px; margin-top:16px;\n  background:var(--raise); border:1px solid var(--edge); border-radius:var(--r-lg);\n  box-shadow:0 30px 60px -24px rgba(0,0,0,.95);\n  opacity:0; visibility:hidden; transform:translateY(-10px) scale(.97); transform-origin:24px top;\n  transition:opacity .25s var(--ease), transform .4s var(--spring), visibility .3s;\n}\n.sub::before{ \/* the little tap-off nipple *\/\n  content:''; position:absolute; top:-5px; left:26px; width:10px; height:10px;\n  background:var(--raise); border-left:1px solid var(--edge); border-top:1px solid var(--edge);\n  transform:rotate(45deg); border-radius:2px;\n}\n.has-sub:hover .sub,\n.has-sub:focus-within .sub{ opacity:1; visibility:visible; transform:translateY(0) scale(1); }\n.has-sub:hover .chev,\n.has-sub:focus-within .chev{ transform:rotate(225deg) translateY(-2px); }\n.sub a{ display:block; padding:11px 14px; border-radius:12px; font-size:.95rem; }\n.sub a:hover{ background:rgba(192,138,78,.14); color:var(--chalk); transform:translateX(5px); }\n\n.nav__call{\n  background:var(--chalk); color:var(--void); font-weight:700; font-size:var(--t-sm);\n  padding:12px 24px; border-radius:var(--r-pill); white-space:nowrap;\n  transition:transform .4s var(--spring), background .25s;\n}\n.nav__call:hover{ transform:translateY(-3px) rotate(1.5deg); background:var(--brass-hi); }\n\n\/* Burger \u2014 turns like a valve handle *\/\n.nav__toggle{\n  display:none; align-items:center; justify-content:center;\n  width:46px; height:46px; margin-right:-8px;\n  background:none; border:0; cursor:pointer; color:var(--chalk);\n  transition:transform .5s var(--spring);\n}\n.nav__toggle span{ position:relative; display:block; width:22px; height:2px; background:currentColor; border-radius:2px;\n  transition:transform .4s var(--spring), opacity .2s; }\n.nav__toggle span::before,\n.nav__toggle span::after{\n  content:''; position:absolute; left:0; width:22px; height:2px; background:currentColor; border-radius:2px;\n  transition:transform .45s var(--spring), width .3s var(--spring);\n}\n.nav__toggle span::before{ top:-7px; }\n.nav__toggle span::after{ top:7px; width:15px; }\n.nav__toggle:hover span::after{ width:22px; }\n.nav__toggle[aria-expanded=\"true\"],\n.nav__cb:checked ~ .nav .nav__toggle{ transform:rotate(90deg); color:var(--brass-hi); }\n.nav__toggle[aria-expanded=\"true\"] span,\n.nav__cb:checked ~ .nav .nav__toggle span{ background:transparent; }\n.nav__toggle[aria-expanded=\"true\"] span::before,\n.nav__cb:checked ~ .nav .nav__toggle span::before{ transform:translateY(7px) rotate(45deg); }\n.nav__toggle[aria-expanded=\"true\"] span::after,\n.nav__cb:checked ~ .nav .nav__toggle span::after{ width:22px; transform:translateY(-7px) rotate(-45deg); }\n.nav__toggle{ cursor:pointer; }\n.nav__toggle:focus-visible{ outline:2px solid var(--brass-hi); outline-offset:4px; }\n\n\/* \u2500\u2500 Mobile panel \u2014 items hang off a brass riser \u2500\u2500 *\/\n.menu{\n  position:fixed; inset:74px 0 0; z-index:99;\n  background:var(--void);\n  overflow-y:auto; -webkit-overflow-scrolling:touch;\n  opacity:0; visibility:hidden;\n  transition:opacity .28s var(--ease), visibility .3s;\n}\n.menu.is-open,\n.nav__cb:checked ~ .menu{ opacity:1; visibility:visible; }\n\n\/* The checkbox itself never shows \u2014 but it must stay in the layout,\n   not display:none, or the :checked selectors below stop matching. *\/\n.nav__cb{ position:absolute; width:1px; height:1px; opacity:0; pointer-events:none; margin:0; }\n\n\/* The sticky call bar sits above the panel, so tuck it away while the\n   menu is open \u2014 otherwise it covers the last menu items. *\/\n.nav__cb:checked ~ .callbar,\nbody.menu-open .callbar{ opacity:0; visibility:hidden; pointer-events:none; }\n.menu__in{ padding:var(--s3) var(--gutter) 140px; }\n\n.menu__list{ position:relative; list-style:none; padding-left:30px; }\n.menu__list::before{           \/* the riser *\/\n  content:''; position:absolute; left:6px; top:16px; bottom:16px; width:4px; border-radius:4px;\n  background:linear-gradient(180deg, var(--brass-hi), var(--brass) 55%, rgba(246,246,244,.12));\n  transform-origin:top; transform:scaleY(0);\n  transition:transform .7s var(--ease);\n}\n.menu.is-open .menu__list::before,\n.nav__cb:checked ~ .menu .menu__list::before{ transform:scaleY(1); }\n\n.menu__row{ position:relative; }\n.menu.is-open .menu__row,\n.nav__cb:checked ~ .menu .menu__row{ animation:dropIn .55s var(--spring) backwards; }\n.menu.is-open .menu__row:nth-child(1),\n.nav__cb:checked ~ .menu .menu__row:nth-child(1){ animation-delay:.06s }\n.menu.is-open .menu__row:nth-child(2),\n.nav__cb:checked ~ .menu .menu__row:nth-child(2){ animation-delay:.11s }\n.menu.is-open .menu__row:nth-child(3),\n.nav__cb:checked ~ .menu .menu__row:nth-child(3){ animation-delay:.16s }\n.menu.is-open .menu__row:nth-child(4),\n.nav__cb:checked ~ .menu .menu__row:nth-child(4){ animation-delay:.21s }\n.menu.is-open .menu__row:nth-child(5),\n.nav__cb:checked ~ .menu .menu__row:nth-child(5){ animation-delay:.26s }\n.menu.is-open .menu__row:nth-child(6),\n.nav__cb:checked ~ .menu .menu__row:nth-child(6){ animation-delay:.31s }\n.menu.is-open .menu__row:nth-child(7),\n.nav__cb:checked ~ .menu .menu__row:nth-child(7){ animation-delay:.36s }\n.menu.is-open .menu__row:nth-child(8),\n.nav__cb:checked ~ .menu .menu__row:nth-child(8){ animation-delay:.41s }\n@keyframes dropIn{\n  from{ opacity:0; transform:translate(-10px,-14px) rotate(-1.6deg); }\n  to  { opacity:1; transform:none; }\n}\n\n.menu__link,\n.menu__acc{\n  position:relative; display:flex; align-items:center; justify-content:space-between; width:100%;\n  padding:19px 4px; font-family:var(--display); font-weight:700;\n  font-size:1.3rem; letter-spacing:-.025em; color:var(--chalk);\n  background:none; border:0; border-bottom:1px solid var(--edge); cursor:pointer; text-align:left;\n  transition:transform .4s var(--spring), color .25s;\n}\n.menu__link::before,\n.menu__acc::before{             \/* the joint where each item taps off the riser *\/\n  content:''; position:absolute; left:-30px; top:50%; width:14px; height:14px; margin-top:-7px;\n  border-radius:50%; background:var(--void); border:3px solid var(--brass);\n  transition:transform .4s var(--spring), background .25s, border-color .25s;\n}\n.menu__link:hover,\n.menu__acc:hover,\n.menu__link:active{ transform:translateX(8px) rotate(-.7deg); color:var(--brass-hi); }\n.menu__link:hover::before,\n.menu__acc:hover::before{ transform:scale(1.35); background:var(--brass-hi); border-color:var(--brass-hi); }\n\n.menu__link.is-ember{ color:var(--ember-hi); }\n.menu__link.is-ember::before{ border-color:var(--ember); }\n.menu__link.is-ember::after{    \/* a drip, because of course *\/\n  content:''; position:absolute; left:-25px; top:50%; width:5px; height:7px; margin-top:-3px;\n  border-radius:50% 50% 50% 50% \/ 62% 62% 38% 38%;\n  background:var(--ember-hi); opacity:0;\n  animation:drip 3.4s var(--ease) infinite;\n}\n@keyframes drip{\n  0%,55%{ opacity:0; transform:translateY(0) scaleY(.6); }\n  62%   { opacity:1; transform:translateY(2px) scaleY(1); }\n  86%   { opacity:1; transform:translateY(22px) scaleY(1.5); }\n  100%  { opacity:0; transform:translateY(30px) scaleY(1.7); }\n}\n\n.menu__acc .chev{ width:11px; height:11px; color:var(--brass); }\n.menu__acc[aria-expanded=\"true\"]{ color:var(--brass-hi); }\n.menu__acc[aria-expanded=\"true\"] .chev{ transform:rotate(225deg) translateY(-2px); color:var(--brass-hi); }\n\n.menu__sub{                     \/* the branch line, elbowed off the riser *\/\n  position:relative; list-style:none; max-height:0; overflow:hidden;\n  margin-left:10px; transition:max-height .45s var(--spring);\n}\n.menu__sub::before{\n  content:''; position:absolute; left:0; top:0; bottom:26px; width:3px;\n  background:rgba(192,138,78,.45); border-radius:3px;\n}\n.menu__sub a{\n  position:relative; display:block; padding:15px 4px 15px 26px; font-size:1rem; font-weight:500;\n  color:var(--chalk-45); border-bottom:1px solid var(--edge);\n  transition:transform .35s var(--spring), color .25s;\n}\n.menu__sub a::before{           \/* elbow *\/\n  content:''; position:absolute; left:0; top:0; width:18px; height:50%;\n  border-left:3px solid rgba(192,138,78,.45);\n  border-bottom:3px solid rgba(192,138,78,.45);\n  border-bottom-left-radius:10px;\n}\n.menu__sub a:hover{ color:var(--brass-hi); transform:translateX(6px); }\n\n.menu__foot{ display:flex; flex-direction:column; gap:14px; margin-top:var(--s5); }\n.menu__foot .btn--light{ background:var(--chalk); color:var(--void); }\n.menu__foot .btn--light:hover{ background:#fff; transform:translateY(-3px) rotate(-1.2deg); }\n.menu__phone{ font-family:var(--display); font-size:1.6rem; font-weight:700; letter-spacing:-.02em;\n  transition:transform .4s var(--spring); }\n.menu__phone:hover{ transform:rotate(-1.5deg) scale(1.02); }\n.menu__phone span{ display:block; font-family:var(--body); font-size:var(--t-xs); font-weight:600;\n  letter-spacing:.1em; text-transform:uppercase; color:var(--brass); margin-top:4px; }\nbody.menu-open{ overflow:hidden; }\n\n\/* \u2500\u2500 Hero \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n.hero{ position:relative; padding-block:clamp(var(--s6), 11vw, 170px) var(--s6); overflow:hidden; }\n.hero__in{ position:relative; z-index:1; }\n\n\/* Ambient mesh \u2014 sits behind the hero copy, never in front of it *\/\n.hero__mesh{\n  position:absolute; inset:0; z-index:0;\n  width:100%; height:100%;\n  display:block; pointer-events:none;\n  opacity:0; transition:opacity 1.2s var(--ease);\n  \/* fade at the edges so it dissolves into the page instead of ending in a hard line *\/\n  -webkit-mask-image:radial-gradient(120% 92% at 62% 42%, #000 30%, rgba(0,0,0,.45) 62%, transparent 88%);\n          mask-image:radial-gradient(120% 92% at 62% 42%, #000 30%, rgba(0,0,0,.45) 62%, transparent 88%);\n}\n.hero__mesh.is-live{ opacity:1; }\n.hero h1{\n  font-size:var(--t-hero); letter-spacing:-.035em; line-height:1.04; margin-top:var(--s3);\n  max-width:15ch;\n}\n.hero h1 em{ display:block; }\n.hero h1 em{ font-style:normal; color:var(--chalk-70); }\n.hero__sub{ color:var(--chalk-70); font-size:clamp(1.05rem,1.5vw,1.3rem); max-width:50ch; margin-top:var(--s4); }\n.hero__ctas{ display:flex; gap:12px; flex-wrap:wrap; margin-top:var(--s5); }\n\n.pill-call{\n  display:inline-flex; align-items:baseline; gap:14px; flex-wrap:wrap;\n  background:var(--chalk); color:var(--void); border-radius:var(--r-pill);\n  padding:15px 32px; transition:transform .35s var(--ease), box-shadow .35s var(--ease);\n  box-shadow:0 0 0 0 rgba(246,246,244,0);\n}\n.pill-call:hover{ transform:translateY(-2px); box-shadow:0 18px 50px -18px rgba(246,246,244,.45); }\n.pill-call b{ font-family:var(--display); font-weight:700; font-size:1.3rem; letter-spacing:-.02em; white-space:nowrap; }\n.pill-call span{ font-size:var(--t-xs); font-weight:600; letter-spacing:.1em; text-transform:uppercase; opacity:.55; }\n\n.facts{\n  display:grid; grid-template-columns:repeat(4,1fr); gap:1px;\n  margin-top:clamp(var(--s6), 8vw, var(--s7));\n  background:var(--edge); border-block:1px solid var(--edge);\n}\n.fact{ background:var(--void); padding:var(--s4) var(--s3) var(--s4) 0; }\n.fact b{ display:block; font-family:var(--display); font-weight:800; font-size:clamp(1.5rem,2.4vw,2.1rem); letter-spacing:-.035em; }\n.fact span{ display:block; color:var(--chalk-45); font-size:var(--t-sm); margin-top:6px; }\n\n\/* \u2500\u2500 Services \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n.grid{ display:grid; grid-template-columns:repeat(4,1fr); gap:14px; margin-top:var(--s6); }\n.card{\n  background:var(--raise); border:1px solid var(--edge); border-radius:var(--r-lg);\n  padding:var(--s4) var(--s3) var(--s4); position:relative; overflow:hidden;\n  transition:transform .45s var(--ease), border-color .35s var(--ease), background .35s var(--ease);\n}\n.card:hover{ transform:translateY(-6px); border-color:var(--edge-hi); }\n.card h3{ font-size:var(--t-h3); }\n.card p{ color:var(--chalk-45); font-size:var(--t-sm); margin-top:12px; line-height:1.55; }\n.card__i{ font-size:var(--t-xs); font-weight:700; letter-spacing:.12em; color:var(--chalk-28); display:block; margin-bottom:var(--s3); }\n\n\/* \u2500\u2500 Who we serve \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n.serve{ display:grid; grid-template-columns:repeat(3,1fr); gap:14px; margin-top:var(--s6); }\n.panel{\n  background:var(--raise);\n  border:1px solid var(--edge); border-radius:var(--r-lg); padding:var(--s5) var(--s4);\n  transition:transform .45s var(--ease), border-color .35s var(--ease);\n}\n.panel:hover{ transform:translateY(-6px); border-color:var(--edge-hi); }\n.panel h3{ font-size:clamp(1.4rem,2.1vw,1.85rem); }\n.panel p{ color:var(--chalk-45); font-size:var(--t-sm); margin-top:14px; }\n.panel ul{ list-style:none; margin-top:var(--s3); }\n.panel ul li{ font-size:var(--t-sm); color:var(--chalk-70); padding:11px 0; border-top:1px solid var(--edge); }\n\n\/* \u2500\u2500 Steps \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n.steps{ display:grid; grid-template-columns:repeat(4,1fr); gap:var(--s4); margin-top:var(--s6); }\n.step b{ font-family:var(--display); font-weight:800; font-size:clamp(2.6rem,4.5vw,3.6rem); letter-spacing:-.05em; color:var(--chalk-28); display:block; line-height:1; }\n.step h3{ font-size:var(--t-h3); margin-top:var(--s2); }\n.step p{ color:var(--chalk-45); font-size:var(--t-sm); margin-top:10px; }\n\n\/* \u2500\u2500 Why \/ split \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n.split{ display:grid; grid-template-columns:1fr 1fr; gap:clamp(var(--s5),6vw,var(--s7)); align-items:start; }\n.reasons{ list-style:none; }\n.reasons li{ padding:var(--s3) 0; border-top:1px solid var(--edge); }\n.reasons li:last-child{ border-bottom:1px solid var(--edge); }\n.reasons h3{ font-size:1.1rem; }\n.reasons p{ color:var(--chalk-45); font-size:var(--t-sm); margin-top:8px; }\n\n.shots{ display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:var(--s5); }\n.shot{\n  aspect-ratio:3\/4; border-radius:var(--r-md); border:1px solid var(--edge);\n  background:var(--raise); display:flex; align-items:center; justify-content:center;\n  color:var(--chalk-28); font-size:var(--t-xs); font-weight:600; letter-spacing:.12em;\n  text-transform:uppercase; text-align:center; overflow:hidden;\n}\n.shot img,\n.shot video{ width:100%; height:100%; object-fit:cover; display:block; background:#000; }\n\n\/* \u2500\u2500 Areas \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n.chips{ display:flex; flex-wrap:wrap; gap:10px; margin-top:var(--s5); }\n.chip{\n  border:1px solid var(--edge); border-radius:var(--r-pill); padding:11px 22px;\n  font-size:var(--t-sm); font-weight:500; color:var(--chalk-70);\n  transition:border-color .3s var(--ease), color .3s var(--ease);\n}\n.chip:hover{ border-color:var(--edge-hi); color:var(--chalk); }\n.chip--home{ background:var(--chalk); color:var(--void); border-color:var(--chalk); font-weight:700; }\n\n\/* \u2500\u2500 Form \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n.quote{ display:grid; grid-template-columns:.85fr 1.15fr; gap:clamp(var(--s5),6vw,var(--s7)); align-items:start; }\n.lines{ list-style:none; margin-top:var(--s5); }\n.lines li{ display:flex; gap:var(--s3); padding:15px 0; border-top:1px solid var(--edge); font-size:var(--t-sm); }\n.lines li em{ font-style:normal; color:var(--chalk-45); min-width:7ch; }\n.form{ background:var(--raise); border:1px solid var(--edge); border-radius:var(--r-lg); padding:clamp(var(--s4),4vw,var(--s5)); }\n.field{ margin-bottom:var(--s2); }\n.field label{ display:block; font-size:var(--t-xs); font-weight:600; letter-spacing:.06em; color:var(--chalk-45); margin-bottom:8px; }\n.field input,.field select,.field textarea{\n  width:100%; background:var(--void); border:1px solid var(--edge); color:var(--chalk);\n  font-family:var(--body); font-size:.98rem; padding:15px 16px; border-radius:12px;\n  transition:border-color .25s;\n}\n.field input:focus,.field select:focus,.field textarea:focus{ outline:none; border-color:var(--chalk-45); }\n.field textarea{ resize:vertical; min-height:120px; }\n.row2{ display:grid; grid-template-columns:1fr 1fr; gap:12px; }\n.form button{\n  width:100%; margin-top:var(--s2); background:var(--chalk); color:var(--void);\n  border:1px solid var(--chalk); border-radius:var(--r-pill);\n  font-family:var(--body); font-weight:700; font-size:var(--t-sm); padding:18px; cursor:pointer;\n  transition:transform .35s var(--ease), background .3s;\n}\n.form button:hover{ transform:translateY(-2px); background:#fff; }\n.form button:disabled{ opacity:.45; cursor:not-allowed; transform:none; }\n\n\/* Chrome paints autofilled fields blue by default, which broke the black-and-white\n   look as soon as a customer's saved details dropped in. There is no property to\n   turn that off, so the standard trick: a very long inset shadow repaints the box. *\/\n.form input:-webkit-autofill,\n.form input:-webkit-autofill:hover,\n.form input:-webkit-autofill:focus,\n.form select:-webkit-autofill,\n.form textarea:-webkit-autofill{\n  -webkit-text-fill-color:var(--chalk);\n  -webkit-box-shadow:0 0 0 60px var(--void) inset;\n          box-shadow:0 0 0 60px var(--void) inset;\n  caret-color:var(--chalk);\n  transition:background-color 9999s ease-in-out 0s;\n}\n.form__note{ text-align:center; color:var(--chalk-28); font-size:var(--t-xs); margin-top:14px; }\n.msg{ display:none; border-radius:12px; padding:var(--s3); font-size:var(--t-sm); margin-bottom:var(--s3); border:1px solid var(--edge-hi); }\n\n\/* \u2500\u2500 FAQ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n.faq{ margin-top:var(--s6); }\n.faq details{ border-top:1px solid var(--edge); }\n.faq details:last-of-type{ border-bottom:1px solid var(--edge); }\n.faq summary{\n  list-style:none; cursor:pointer; padding:var(--s3) 0; display:flex; gap:var(--s3);\n  align-items:center; justify-content:space-between;\n  font-family:var(--display); font-weight:700; font-size:clamp(1.05rem,1.6vw,1.3rem); letter-spacing:-.025em;\n}\n.faq summary::-webkit-details-marker{ display:none; }\n.faq summary::after{\n  content:''; flex:none; width:11px; height:11px; border-right:2px solid var(--chalk-45);\n  border-bottom:2px solid var(--chalk-45); transform:rotate(45deg) translateY(-3px);\n  transition:transform .35s var(--ease);\n}\n.faq details[open] summary::after{ transform:rotate(225deg) translateY(-3px); }\n.faq details p{ color:var(--chalk-45); font-size:var(--t-sm); padding-bottom:var(--s3); max-width:80ch; }\n\n\/* \u2500\u2500 CTA \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n.cta{ position:relative; text-align:center; padding-block:clamp(var(--s6),9vw,var(--s8)); overflow:hidden; }\n.cta__in{ position:relative; }\n.cta h2{ font-size:var(--t-h2); max-width:18ch; margin-inline:auto; }\n.cta p{ color:var(--chalk-70); margin:var(--s3) auto 0; max-width:50ch; }\n.cta__btns{ display:flex; gap:12px; justify-content:center; flex-wrap:wrap; margin-top:var(--s5); }\n\n\/* \u2500\u2500 Footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n.footer{ background:var(--matte); border-top:1px solid var(--edge); padding-block:var(--s6) var(--s3); }\n.footer__grid{ display:grid; grid-template-columns:1.7fr 1fr 1fr 1fr; gap:var(--s5); }\n.footer p{ color:var(--chalk-45); font-size:var(--t-sm); margin-top:var(--s3); max-width:34ch; }\n.footer__tel{ display:inline-block; font-family:var(--display); font-weight:800; font-size:1.5rem; letter-spacing:-.03em; margin-top:var(--s3); }\n.footer h4{ font-size:var(--t-xs); font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--chalk-28); margin-bottom:var(--s3); }\n.footer ul{ list-style:none; }\n.footer ul li{ margin-bottom:11px; }\n.footer ul a{ font-size:var(--t-sm); color:var(--chalk-70); }\n.footer ul a:hover{ color:var(--chalk); }\n.footer__base{\n  display:flex; justify-content:space-between; gap:var(--s3); flex-wrap:wrap;\n  border-top:1px solid var(--edge); margin-top:var(--s6); padding-top:var(--s3);\n  font-size:var(--t-xs); color:var(--chalk-28);\n}\n\n\/* Sticky call bar \u2014 mobile only. A plumbing site's whole job. *\/\n.callbar{\n  display:none; position:fixed; left:12px; right:12px; bottom:12px; z-index:120;\n  background:var(--chalk); color:var(--void); border-radius:var(--r-pill);\n  padding:16px; text-align:center; font-weight:700; font-size:1rem;\n  box-shadow:0 20px 50px -18px rgba(0,0,0,.9);\n}\n\n\n\/* \u2500\u2500 Responsive \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n@media (max-width:1023px){\n  .nav__links{ display:none; }\n  .nav__toggle{ display:inline-flex; }\n  .nav__call{ font-size:.95rem; padding:11px 18px; }\n}\n@media (max-width:1180px){\n  .grid{ grid-template-columns:repeat(2,1fr); }\n  .serve{ grid-template-columns:1fr; }\n  .steps{ grid-template-columns:repeat(2,1fr); gap:var(--s5) var(--s4); }\n  .split,.quote{ grid-template-columns:1fr; }\n  .footer__grid{ grid-template-columns:1fr 1fr; }\n}\n@media (max-width:760px){\n  .nav__call{ display:none; }\n  :root{ --s6:56px; --s7:68px; --s8:78px; }\n  .hero{ padding-block:44px 40px; }\n  .hero__ctas{ flex-direction:column; align-items:stretch; }\n  .pill-call{ justify-content:space-between; }\n  .btn{ justify-content:center; }\n  .card{ padding:28px 22px; }\n  .facts{ grid-template-columns:1fr 1fr; }\n  .fact{ padding-inline:var(--s2) 0; }\n  .grid{ grid-template-columns:1fr; }\n  .steps{ grid-template-columns:1fr; }\n  .row2{ grid-template-columns:1fr; }\n  .footer__grid{ grid-template-columns:1fr; }\n  .callbar{ display:block; }\n  body{ padding-bottom:84px; }\n  .hero h1{ max-width:100%; }\n}\n@media (prefers-reduced-motion:reduce){\n  html{ scroll-behavior:auto; }\n  *,*::before,*::after{ animation:none !important; transition:none !important; }\n}\n<\/style>\n\n<!-- Drives the mobile menu with CSS alone, so the panel still opens\n     if JavaScript is blocked (in-app browsers, file previews, etc.). -->\n<input type=\"checkbox\" id=\"navToggleCb\" class=\"nav__cb\" aria-hidden=\"true\" tabindex=\"-1\">\n\n<!-- \u2550\u2550\u2550 NAV \u2550\u2550\u2550 -->\n<nav class=\"nav\">\n  <div class=\"wrap nav__in\">\n    <a href=\"\/\" class=\"logo\">Onex<em>Plumbing<\/em><\/a>\n\n    <!-- Desktop links. Anchors point at the one-page sections for now;\n         swap the href to the real page path as each page goes live. -->\n    <ul class=\"nav__links\">\n      <li><a href=\"#why\">About<\/a><\/li>\n      <li><a href=\"#serve\">Residential<\/a><\/li>\n      <li><a href=\"#serve\">Commercial &amp; strata<\/a><\/li>\n      <li class=\"has-sub\">\n        <button class=\"sub__btn\" aria-expanded=\"false\">Services <i class=\"chev\"><\/i><\/button>\n        <ul class=\"sub\">\n          <li><a href=\"#services\">Drain cleaning &amp; camera<\/a><\/li>\n          <li><a href=\"#services\">Hot water tanks<\/a><\/li>\n          <li><a href=\"#services\">Heating &amp; heat pumps<\/a><\/li>\n          <li><a href=\"#services\">Sewer &amp; underground<\/a><\/li>\n          <li><a href=\"#services\">Repairs &amp; renovations<\/a><\/li>\n        <\/ul>\n      <\/li>\n      <li><a href=\"#areas\">Service areas<\/a><\/li>\n      <li><a href=\"#quote\">Free quote<\/a><\/li>\n      <li><a href=\"tel:6043388408\" class=\"is-ember\">24\/7 emergency<\/a><\/li>\n    <\/ul>\n\n    <a href=\"tel:6043388408\" class=\"nav__call\">(604) 338-8408<\/a>\n\n    <label class=\"nav__toggle\" id=\"navToggle\" for=\"navToggleCb\" role=\"button\" tabindex=\"0\"\n           aria-expanded=\"false\" aria-controls=\"menu\" aria-label=\"Open menu\">\n      <span><\/span>\n    <\/label>\n  <\/div>\n<\/nav>\n\n<!-- \u2550\u2550\u2550 MOBILE MENU \u2550\u2550\u2550 -->\n<div class=\"menu\" id=\"menu\">\n  <div class=\"menu__in\">\n    <ul class=\"menu__list\">\n      <li class=\"menu__row\"><a class=\"menu__link\" href=\"\/\">Home<\/a><\/li>\n      <li class=\"menu__row\"><a class=\"menu__link\" href=\"#why\">About<\/a><\/li>\n      <li class=\"menu__row\"><a class=\"menu__link\" href=\"#serve\">Residential<\/a><\/li>\n      <li class=\"menu__row\"><a class=\"menu__link\" href=\"#serve\">Commercial &amp; strata<\/a><\/li>\n      <li class=\"menu__row\">\n        <button class=\"menu__acc\" aria-expanded=\"false\" aria-controls=\"menuServices\">\n          Services <i class=\"chev\"><\/i>\n        <\/button>\n        <ul class=\"menu__sub\" id=\"menuServices\">\n          <li><a href=\"#services\">Drain cleaning &amp; camera<\/a><\/li>\n          <li><a href=\"#services\">Hot water tanks<\/a><\/li>\n          <li><a href=\"#services\">Heating &amp; heat pumps<\/a><\/li>\n          <li><a href=\"#services\">Sewer &amp; underground<\/a><\/li>\n          <li><a href=\"#services\">Repairs &amp; renovations<\/a><\/li>\n        <\/ul>\n      <\/li>\n      <li class=\"menu__row\"><a class=\"menu__link\" href=\"#areas\">Service areas<\/a><\/li>\n      <li class=\"menu__row\"><a class=\"menu__link\" href=\"#process\">How it works<\/a><\/li>\n      <li class=\"menu__row\"><a class=\"menu__link is-ember\" href=\"tel:6043388408\">24\/7 emergency<\/a><\/li>\n    <\/ul>\n\n    <div class=\"menu__foot\">\n      <a href=\"#quote\" class=\"btn btn--light\">Get a free quote<\/a>\n      <a href=\"tel:6043388408\" class=\"menu__phone\">(604) 338-8408<span>Tap to call &mdash; 24\/7<\/span><\/a>\n    <\/div>\n  <\/div>\n<\/div>\n\n<!-- \u2550\u2550\u2550 HERO \u2550\u2550\u2550 -->\n<header class=\"hero\">\n  <canvas class=\"hero__mesh\" id=\"heroMesh\" aria-hidden=\"true\"><\/canvas>\n  <div class=\"wrap hero__in\">\n    <span class=\"eyebrow rise\">Plumbing &middot; Drainage &middot; Heating<\/span>\n    <h1 class=\"rise\">Plumbers in Nanaimo<em>24 hours a day.<\/em><\/h1>\n    <p class=\"hero__sub rise\">Licensed plumbing, drainage and heating for homes, stratas and businesses across Vancouver Island. One fixed price in writing before we start.<\/p>\n\n    <div class=\"hero__ctas rise\">\n      <a href=\"tel:6043388408\" class=\"pill-call\">\n        <b>(604) 338-8408<\/b>\n        <span>Tap to call &mdash; 24\/7<\/span>\n      <\/a>\n      <a href=\"#quote\" class=\"btn btn--line\">Get a free quote<\/a>\n    <\/div>\n\n    <div class=\"facts rise\">\n      <div class=\"fact\"><b>24\/7<\/b><span>Emergency call-out<\/span><\/div>\n      <div class=\"fact\"><b>8+ yrs<\/b><span>On the tools<\/span><\/div>\n      <div class=\"fact\"><b>Licensed<\/b><span>Skilled Trades BC<\/span><\/div>\n      <div class=\"fact\"><b>Insured<\/b><span>WorkSafeBC compliant<\/span><\/div>\n    <\/div>\n  <\/div>\n<\/header>\n\n<!-- \u2550\u2550\u2550 ESTIMATE \u2550\u2550\u2550 -->\n<section class=\"band\" id=\"quote\">\n  <div class=\"wrap quote\">\n    <div>\n      <span class=\"eyebrow rise\">Free quote<\/span>\n      <h2 class=\"h2 rise\">Get a free quote<\/h2>\n      <p class=\"lede rise\">Send the details and we will come back with a scope and a fixed price. If water is moving right now, call instead &mdash; the phone is faster.<\/p>\n      <ul class=\"lines rise\">\n        <li><em>Phone<\/em><a href=\"tel:6043388408\">(604) 338-8408 &mdash; 24\/7<\/a><\/li>\n        <li><em>Email<\/em><a href=\"mailto:info@onexmechanical.ca\">info@onexmechanical.ca<\/a><\/li>\n        <li><em>Based<\/em>Nanaimo, BC<\/li>\n        <li><em>Hours<\/em>Emergency dispatch, day and night<\/li>\n      <\/ul>\n    <\/div>\n\n    <div class=\"form rise\">\n      <div class=\"msg\" id=\"formError\">\n        This form could not send. Your details are safe \u2014 <a href=\"#\" id=\"formMailto\">send them by email instead<\/a>,\n        or call <a href=\"tel:6043388408\">(604) 338-8408<\/a> and we will take it over the phone.\n      <\/div>\n      <div class=\"msg\" id=\"formSuccess\">Request received. We will be in touch within the hour.<\/div>\n      <!-- \u26a0 SET THIS BEFORE LAUNCH \u26a0\n           Create a form at formspree.io, copy the endpoint it gives you, and paste it\n           over the action below. Until that is done, every submit falls back to a\n           pre-filled email to info@onexmechanical.ca, so no lead is lost either way. -->\n      <form id=\"quoteForm\" action=\"https:\/\/formspree.io\/f\/YOUR_FORM_ID\" method=\"POST\">\n        <input type=\"hidden\" name=\"_subject\" value=\"New quote request \u2014 onexmechanical.ca\">\n        <div class=\"row2\">\n          <div class=\"field\"><label for=\"f-name\">Name<\/label><input id=\"f-name\" name=\"name\" type=\"text\" required><\/div>\n          <div class=\"field\"><label for=\"f-phone\">Phone<\/label><input id=\"f-phone\" name=\"phone\" type=\"tel\" required><\/div>\n        <\/div>\n        <div class=\"row2\">\n          <div class=\"field\"><label for=\"f-email\">Email<\/label><input id=\"f-email\" name=\"email\" type=\"email\"><\/div>\n          <div class=\"field\"><label for=\"f-city\">City or area<\/label><input id=\"f-city\" name=\"city\" type=\"text\"><\/div>\n        <\/div>\n        <div class=\"field\">\n          <label for=\"f-prop\">Property type<\/label>\n          <select id=\"f-prop\" name=\"property_type\" required>\n            <option value=\"\" disabled selected>Select&hellip;<\/option>\n            <option>House<\/option>\n            <option>Condo or townhouse<\/option>\n            <option>Rental or suite<\/option>\n            <option>Strata building<\/option>\n            <option>Commercial or retail<\/option>\n            <option>School or institution<\/option>\n            <option>New build or renovation<\/option>\n            <option>Other<\/option>\n          <\/select>\n        <\/div>\n        <div class=\"field\">\n          <label for=\"f-service\">Service needed<\/label>\n          <select id=\"f-service\" name=\"service\" required>\n            <option value=\"\" disabled selected>Select&hellip;<\/option>\n            <option>Emergency plumbing<\/option>\n            <option>Drain or sewer<\/option>\n            <option>Hot water tank<\/option>\n            <option>Heating or furnace<\/option>\n            <option>Heat pump or AC<\/option>\n            <option>Gas fitting<\/option>\n            <option>Renovation or new build<\/option>\n            <option>Maintenance contract<\/option>\n            <option>Other<\/option>\n          <\/select>\n        <\/div>\n        <div class=\"field\"><label for=\"f-msg\">Describe the issue<\/label><textarea id=\"f-msg\" name=\"message\" rows=\"4\"><\/textarea><\/div>\n        <button type=\"submit\" id=\"submitBtn\">Get my free quote<\/button>\n        <p class=\"form__note\">We reply within the hour &middot; No obligation<\/p>\n      <\/form>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- \u2550\u2550\u2550 SERVICES \u2550\u2550\u2550 -->\n<section class=\"band band--matte\" id=\"services\">\n  <div class=\"wrap\">\n    <span class=\"eyebrow rise\">Services<\/span>\n    <h2 class=\"h2 rise\">Every plumbing job, start to finish<\/h2>\n    <p class=\"lede rise\">From a dripping tap to a failed sewer main. We install it, service it, or replace it &mdash; one number, no subcontractor runaround.<\/p>\n\n    <div class=\"grid\">\n      <article class=\"card rise\"><span class=\"card__i\">01<\/span><h3>Emergency plumbing<\/h3><p>Burst pipes, flooding, sewage backup. We answer at any hour, isolate the problem fast, and stop the damage before it spreads.<\/p><\/article>\n      <article class=\"card rise\"><span class=\"card__i\">02<\/span><h3>Drain &amp; sewer<\/h3><p>Drain cleaning, hydro-jetting, camera inspection and line repair. We find the actual blockage instead of guessing at it.<\/p><\/article>\n      <article class=\"card rise\"><span class=\"card__i\">03<\/span><h3>Hot water tanks<\/h3><p>Tank, tankless and heat-pump water heaters. Supply, install, repair and replace &mdash; usually the same day.<\/p><\/article>\n      <article class=\"card rise\"><span class=\"card__i\">04<\/span><h3>Heating &amp; furnace<\/h3><p>Furnaces, boilers, hydronic and radiant systems. Service, repair, and full replacement before the cold sets in.<\/p><\/article>\n      <article class=\"card rise\"><span class=\"card__i\">05<\/span><h3>Heat pumps &amp; AC<\/h3><p>Ductless mini-splits, central systems and heat pump conversions. Sized to the building, not to a sales target.<\/p><\/article>\n      <article class=\"card rise\"><span class=\"card__i\">06<\/span><h3>Gas fitting<\/h3><p>Gas lines for ranges, fireplaces, BBQs and appliances. Permitted, pressure-tested and signed off.<\/p><\/article>\n      <article class=\"card rise\"><span class=\"card__i\">07<\/span><h3>Renovations &amp; new build<\/h3><p>Full rough-in and fixture sets for bathroom and kitchen renovations, suites, additions and tenant improvements.<\/p><\/article>\n      <article class=\"card rise\"><span class=\"card__i\">08<\/span><h3>Maintenance contracts<\/h3><p>Scheduled service and priority dispatch for stratas, property managers and commercial portfolios.<\/p><\/article>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- \u2550\u2550\u2550 WHO WE SERVE \u2550\u2550\u2550 -->\n<section class=\"band\" id=\"serve\">\n  <div class=\"wrap\">\n    <span class=\"eyebrow rise\">Who we serve<\/span>\n    <h2 class=\"h2 rise\">Houses, buildings, and everything between<\/h2>\n    <p class=\"lede rise\">The same licensed crew, whether it is a leaking kitchen tap or a sanitary main under a shopping centre.<\/p>\n\n    <div class=\"serve\">\n      <div class=\"panel rise\">\n        <h3>Homes<\/h3>\n        <p>Owners, renters and landlords across the Island.<\/p>\n        <ul>\n          <li>Single-family houses<\/li>\n          <li>Townhouses and condos<\/li>\n          <li>Suites and rentals<\/li>\n          <li>Renovations and additions<\/li>\n          <li>Acreages and well systems<\/li>\n        <\/ul>\n      <\/div>\n      <div class=\"panel rise\">\n        <h3>Stratas &amp; rentals<\/h3>\n        <p>Buildings where a leak affects more than one household.<\/p>\n        <ul>\n          <li>Strata corporations<\/li>\n          <li>Property managers<\/li>\n          <li>High-rise and low-rise<\/li>\n          <li>Common-area mains and risers<\/li>\n          <li>Parkade drainage<\/li>\n        <\/ul>\n      <\/div>\n      <div class=\"panel rise\">\n        <h3>Commercial<\/h3>\n        <p>Places that lose money the moment the water stops.<\/p>\n        <ul>\n          <li>Schools and institutions<\/li>\n          <li>Retail and shopping centres<\/li>\n          <li>Restaurants and grease lines<\/li>\n          <li>Service stations and wash bays<\/li>\n          <li>Contractors and restoration firms<\/li>\n        <\/ul>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- \u2550\u2550\u2550 PROCESS \u2550\u2550\u2550 -->\n<section class=\"band band--matte\" id=\"process\">\n  <div class=\"wrap\">\n    <span class=\"eyebrow rise\">How it works<\/span>\n    <h2 class=\"h2 rise\">One price, in writing, before we start<\/h2>\n    <p class=\"lede rise\">You approve a number. That is the number on the invoice. We do not pad quotes to leave room for haggling, and we do not revise them upward halfway through.<\/p>\n\n    <div class=\"steps\">\n      <div class=\"step rise\"><b>01<\/b><h3>Call us<\/h3><p>Any hour. Describe what is happening and a licensed plumber tells you straight away whether it can wait.<\/p><\/div>\n      <div class=\"step rise\"><b>02<\/b><h3>We look<\/h3><p>We attend, diagnose with proper tools, and find the cause rather than treating the symptom.<\/p><\/div>\n      <div class=\"step rise\"><b>03<\/b><h3>Fixed quote<\/h3><p>A written scope and one fixed price. Approve it or don&#8217;t &mdash; there is nothing to negotiate.<\/p><\/div>\n      <div class=\"step rise\"><b>04<\/b><h3>Done and clean<\/h3><p>We complete the work, tidy the site, walk you through it, and leave documentation behind.<\/p><\/div>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- \u2550\u2550\u2550 WHY \u2550\u2550\u2550 -->\n<section class=\"band\" id=\"why\">\n  <div class=\"wrap split\">\n    <div>\n      <span class=\"eyebrow rise\">Why Onex<\/span>\n      <h2 class=\"h2 rise\">A plumber, not a call centre<\/h2>\n      <p class=\"lede rise\">Onex is owner-operated. The person who quotes your job is accountable for it, and you have their number afterwards.<\/p>\n      <div class=\"shots rise\">\n        <!-- Real job photos and video. All files sit beside index.html \u2014 keep the names as they are. -->\n        <div class=\"shot\">\n          <img loading=\"lazy\" src=\"onex-sink-drain-repair.jpg\" alt=\"Onex plumber replacing the drain tailpiece and strainer under a stainless steel sink\" loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"1600\">\n        <\/div>\n        <div class=\"shot\">\n          <video poster=\"IMG_0346-poster.jpg\" muted loop playsinline controls preload=\"metadata\"\n                 aria-label=\"Toilet installation by Onex Mechanical\">\n            <source src=\"IMG_0346.mp4\" type=\"video\/mp4\">\n          <\/video>\n        <\/div>\n        <div class=\"shot\">\n          <img loading=\"lazy\" src=\"IMG_0037.jpg\" alt=\"Commercial mechanical room hot water storage tank and piping installed by Onex Mechanical\" loading=\"lazy\" decoding=\"async\" width=\"1052\" height=\"1400\">\n        <\/div>\n        <div class=\"shot\">\n          <img loading=\"lazy\" src=\"IMG_0347.jpg\" alt=\"New Grohe basin faucet installed on a bathroom vanity\" loading=\"lazy\" decoding=\"async\" width=\"1050\" height=\"1400\">\n        <\/div>\n        <div class=\"shot\">\n          <img loading=\"lazy\" src=\"IMG_0497.jpg\" alt=\"Cast iron P-trap and drain line under a sink \u2014 the cause of drain odour\" loading=\"lazy\" decoding=\"async\" width=\"1089\" height=\"1400\">\n        <\/div>\n      <\/div>\n    <\/div>\n    <ul class=\"reasons\">\n      <li class=\"rise\"><h3>Fixed written pricing<\/h3><p>The quote you approve is the invoice you receive. No surprise line items once the wall is open.<\/p><\/li>\n      <li class=\"rise\"><h3>We actually answer<\/h3><p>Emergencies do not keep business hours. Calls are answered around the clock by someone who can help.<\/p><\/li>\n      <li class=\"rise\"><h3>Root cause, not resets<\/h3><p>Camera inspection and proper diagnosis before we quote. A second visit for the same fault costs us too.<\/p><\/li>\n      <li class=\"rise\"><h3>Licensed and covered<\/h3><p>Skilled Trades BC licensed, fully insured, WorkSafeBC compliant. Certificates available on request.<\/p><\/li>\n      <li class=\"rise\"><h3>Clean when we leave<\/h3><p>Drop sheets, boot covers, and the site left the way we found it. Photos and notes on every job.<\/p><\/li>\n    <\/ul>\n  <\/div>\n<\/section>\n\n<!-- \u2550\u2550\u2550 AREAS \u2550\u2550\u2550 -->\n<section class=\"band band--matte\" id=\"areas\">\n  <div class=\"wrap\">\n    <span class=\"eyebrow rise\">Service area<\/span>\n    <h2 class=\"h2 rise\">Nanaimo and across the Island<\/h2>\n    <p class=\"lede rise\">Based in Nanaimo, covering Ladysmith through Qualicum Beach as standard, and travelling further up-Island for project and contract work.<\/p>\n    <div class=\"chips rise\">\n      <span class=\"chip chip--home\">Nanaimo<\/span>\n      <span class=\"chip\">Lantzville<\/span>\n      <span class=\"chip\">Nanoose Bay<\/span>\n      <span class=\"chip\">Parksville<\/span>\n      <span class=\"chip\">Qualicum Beach<\/span>\n      <span class=\"chip\">Ladysmith<\/span>\n      <span class=\"chip\">Chemainus<\/span>\n      <span class=\"chip\">Duncan<\/span>\n      <span class=\"chip\">Cedar<\/span>\n      <span class=\"chip\">Cassidy<\/span>\n      <span class=\"chip\">Gabriola Island<\/span>\n      <span class=\"chip\">Port Alberni<\/span>\n      <span class=\"chip\">Courtenay<\/span>\n      <span class=\"chip\">Comox<\/span>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- \u2550\u2550\u2550 FAQ \u2550\u2550\u2550 -->\n<section class=\"band\">\n  <div class=\"wrap\">\n    <span class=\"eyebrow rise\">Questions<\/span>\n    <h2 class=\"h2 rise\">Straight answers<\/h2>\n    <div class=\"faq\">\n      <details open class=\"rise\"><summary>Do you offer 24\/7 emergency plumbing in Nanaimo?<\/summary><p>Yes. We take emergency calls around the clock across Nanaimo and mid-Vancouver Island, for homes, stratas and commercial buildings alike. Call (604) 338-8408 any time.<\/p><\/details>\n      <details class=\"rise\"><summary>How much does a plumber cost?<\/summary><p>It depends entirely on the job, which is why we quote before we start rather than after. You get one written price up front, and that is what you are invoiced. No hourly surprises and no extra line items for work that was always part of the job.<\/p><\/details>\n      <details class=\"rise\"><summary>Are you licensed and insured?<\/summary><p>Yes. Licensed through Skilled Trades BC, carrying full liability insurance, and WorkSafeBC compliant. We are happy to send certificates before we attend.<\/p><\/details>\n      <details class=\"rise\"><summary>Do you work on both houses and commercial buildings?<\/summary><p>Both. Single-family homes, condos, rentals, strata buildings, retail units, schools and service stations &mdash; the same licensed crew handles all of it.<\/p><\/details>\n      <details class=\"rise\"><summary>Can you replace a hot water tank the same day?<\/summary><p>Usually yes. Tank, tankless and heat-pump water heaters from all major brands, with same-day replacement available in most cases when you call early.<\/p><\/details>\n      <details class=\"rise\"><summary>How far do you travel?<\/summary><p>Ladysmith through Qualicum Beach is standard coverage, including Gabriola Island. For larger projects and contract work we travel further up-Island &mdash; just ask when you call.<\/p><\/details>\n      <details class=\"rise\"><summary>Do you take on strata and property management contracts?<\/summary><p>Yes. We work with strata corporations and property managers on scheduled maintenance and priority dispatch, with consistent reporting and invoicing across every building in the portfolio.<\/p><\/details>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- \u2550\u2550\u2550 CTA \u2550\u2550\u2550 -->\n<section class=\"cta\">\n  <div class=\"wrap cta__in\">\n    <h2 class=\"rise\">Get it fixed properly, once<\/h2>\n    <p class=\"rise\">Emergency or planned, house or high-rise &mdash; tell us what is happening and we will give you a straight price.<\/p>\n    <div class=\"cta__btns rise\">\n      <a href=\"tel:6043388408\" class=\"btn btn--light\">Call (604) 338-8408<\/a>\n      <a href=\"#quote\" class=\"btn btn--line\">Get a free quote<\/a>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- \u2550\u2550\u2550 FOOTER \u2550\u2550\u2550 -->\n<footer class=\"footer\">\n  <div class=\"wrap\">\n    <div class=\"footer__grid\">\n      <div>\n        <span class=\"logo\">Onex<em>Plumbing<\/em><\/span>\n        <p>Licensed and insured plumbing, drainage, heating and cooling for homes, stratas and businesses across Nanaimo and Vancouver Island. Owner-operated. Available 24\/7.<\/p>\n        <a href=\"tel:6043388408\" class=\"footer__tel\">(604) 338-8408<\/a>\n      <\/div>\n      <div><h4>Services<\/h4><ul>\n        <li><a href=\"#services\">Emergency plumbing<\/a><\/li>\n        <li><a href=\"#services\">Drain &amp; sewer<\/a><\/li>\n        <li><a href=\"#services\">Hot water tanks<\/a><\/li>\n        <li><a href=\"#services\">Heating &amp; furnace<\/a><\/li>\n        <li><a href=\"#services\">Heat pumps &amp; AC<\/a><\/li>\n        <li><a href=\"#services\">Gas fitting<\/a><\/li>\n      <\/ul><\/div>\n      <div><h4>Service area<\/h4><ul>\n        <li><a href=\"#areas\">Nanaimo<\/a><\/li>\n        <li><a href=\"#areas\">Parksville<\/a><\/li>\n        <li><a href=\"#areas\">Qualicum Beach<\/a><\/li>\n        <li><a href=\"#areas\">Ladysmith<\/a><\/li>\n        <li><a href=\"#areas\">Duncan<\/a><\/li>\n        <li><a href=\"#areas\">Gabriola Island<\/a><\/li>\n      <\/ul><\/div>\n      <div><h4>Company<\/h4><ul>\n        <li><a href=\"#why\">About<\/a><\/li>\n        <li><a href=\"#serve\">Who we serve<\/a><\/li>\n        <li><a href=\"#process\">How it works<\/a><\/li>\n        <li><a href=\"#quote\">Free quote<\/a><\/li>\n        <li><a href=\"mailto:info@onexmechanical.ca\">Email us<\/a><\/li>\n      <\/ul><\/div>\n    <\/div>\n    <div class=\"footer__base\">\n      <span>&copy; 2026 Onex Plumbing &mdash; Nanaimo, BC<\/span>\n      <span>Licensed &amp; Insured &middot; WorkSafeBC Compliant<\/span>\n    <\/div>\n  <\/div>\n<\/footer>\n\n<a href=\"tel:6043388408\" class=\"callbar\">Call now &mdash; (604) 338-8408<\/a>\n\n<script>\n\/* \u2500\u2500 Hero mesh \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   A lattice of brass nodes that springs away from the\n   pointer and settles back. On touch devices (and while\n   the pointer is idle) a slow drifting current keeps it\n   alive on its own. Skipped entirely for reduced motion,\n   and paused whenever the hero is off screen.\n   \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n(function(){\n  var canvas = document.getElementById('heroMesh');\n  if(!canvas || !canvas.getContext) return;\n  if(window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;\n\n  var hero = canvas.parentElement;\n  var ctx  = canvas.getContext('2d');\n  var BRASS = '246,246,244';\n  var CHALK = '246,246,244';\n\n  var w = 0, h = 0, spacing = 60;\n  var nodes = [], links = [];\n  var raf = 0, running = false, last = 0;\n  var coarse = window.matchMedia('(pointer: coarse)').matches;\n\n  var ptr = { x:-9999, y:-9999, px:-9999, py:-9999, vx:0, vy:0, r:210, idle:9 };\n\n  function build(){\n    var rect = hero.getBoundingClientRect();\n    w = Math.max(1, Math.round(rect.width));\n    h = Math.max(1, Math.round(rect.height));\n    var dpr = Math.min(window.devicePixelRatio || 1, 2);\n\n    canvas.width  = w * dpr;\n    canvas.height = h * dpr;\n    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n\n    spacing = w < 640 ? 74 : (w < 1100 ? 64 : 58);\n    var cols = Math.ceil(w \/ spacing) + 2;\n    var rows = Math.ceil(h \/ spacing) + 2;\n    var offX = (w - (cols - 1) * spacing) \/ 2;\n    var offY = (h - (rows - 1) * spacing) \/ 2;\n\n    nodes = []; links = [];\n    for(var j = 0; j < rows; j++){\n      for(var i = 0; i < cols; i++){\n        var x = offX + i * spacing, y = offY + j * spacing;\n        nodes.push({ x:x, y:y, vx:0, vy:0, bx:x, by:y,\n                     r: Math.random() * 0.9 + 1.0,\n                     pulse: Math.random() * Math.PI * 2 });\n      }\n    }\n    \/* Lattice neighbours only \u2014 no O(n\u00b2) sweep every frame *\/\n    for(var j2 = 0; j2 < rows; j2++){\n      for(var i2 = 0; i2 < cols; i2++){\n        var k = j2 * cols + i2;\n        if(i2 + 1 < cols) links.push([k, k + 1]);\n        if(j2 + 1 < rows) links.push([k, k + cols]);\n      }\n    }\n  }\n\n  function frame(now){\n    var dt = Math.min((now - last) \/ 1000, 0.05); last = now;\n    ptr.idle += dt;\n\n    \/* Idle current: a slow drift that keeps the mesh breathing\n       when nobody is pointing at it (always the case on phones) *\/\n    if(coarse || ptr.idle > 2.2){\n      var t = now \/ 1000;\n      ptr.x = w * (0.5 + 0.36 * Math.sin(t * 0.31));\n      ptr.y = h * (0.5 + 0.30 * Math.cos(t * 0.23));\n    }\n\n    ptr.vx = (ptr.x - ptr.px) \/ (dt || 1);\n    ptr.vy = (ptr.y - ptr.py) \/ (dt || 1);\n    ptr.px = ptr.x; ptr.py = ptr.y;\n    var speed = Math.min(Math.sqrt(ptr.vx * ptr.vx + ptr.vy * ptr.vy), 2600);\n\n    var damp = Math.pow(0.82, dt * 60);\n    var K = 17, i, n;\n\n    for(i = 0; i < nodes.length; i++){\n      n = nodes[i];\n      n.pulse += dt * 2.2;\n\n      var dx = ptr.x - n.x, dy = ptr.y - n.y;\n      var dist = Math.sqrt(dx * dx + dy * dy);\n\n      if(dist < ptr.r &#038;&#038; dist > 0.01){\n        var force = (1 - dist \/ ptr.r) * (620 + speed * 0.34);\n        n.vx -= (dx \/ dist) * force * dt;\n        n.vy -= (dy \/ dist) * force * dt;\n      }\n      n.vx += (n.bx - n.x) * K * dt;\n      n.vy += (n.by - n.y) * K * dt;\n      n.vx *= damp; n.vy *= damp;\n      n.x  += n.vx * dt * 60;\n      n.y  += n.vy * dt * 60;\n    }\n\n    ctx.clearRect(0, 0, w, h);\n\n    \/* Links *\/\n    var slack = spacing * 1.7;\n    ctx.lineWidth = 0.8;\n    for(i = 0; i < links.length; i++){\n      var a = nodes[links[i][0]], b = nodes[links[i][1]];\n      var lx = a.x - b.x, ly = a.y - b.y;\n      var d = Math.sqrt(lx * lx + ly * ly);\n      if(d > slack) continue;\n      var stretched = Math.abs(d - spacing) \/ spacing;\n      var alpha = (1 - d \/ slack) * 0.16;\n      ctx.strokeStyle = stretched > 0.14\n        ? 'rgba(' + BRASS + ',' + (alpha + stretched * 0.30).toFixed(3) + ')'\n        : 'rgba(' + CHALK + ',' + alpha.toFixed(3) + ')';\n      ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke();\n    }\n\n    \/* Nodes *\/\n    for(i = 0; i < nodes.length; i++){\n      n = nodes[i];\n      var ndx = ptr.x - n.x, ndy = ptr.y - n.y;\n      var nd = Math.sqrt(ndx * ndx + ndy * ndy);\n      var near = nd < ptr.r;\n      var a2 = near ? 0.30 + (1 - nd \/ ptr.r) * 0.62\n                    : 0.16 + Math.sin(n.pulse) * 0.06;\n      ctx.fillStyle = 'rgba(' + (near ? BRASS : CHALK) + ',' + a2.toFixed(3) + ')';\n      var rad = near ? n.r * (1 + (1 - nd \/ ptr.r) * 1.5)\n                     : n.r + Math.sin(n.pulse) * 0.25;\n      ctx.beginPath(); ctx.arc(n.x, n.y, Math.max(0.4, rad), 0, 6.2832); ctx.fill();\n\n      \/* A single ring on whatever the pointer is closest to *\/\n      if(nd < 62){\n        var ring = ((n.pulse * 16) % 26) + 5;\n        ctx.strokeStyle = 'rgba(' + BRASS + ',' + ((1 - ring \/ 32) * 0.32).toFixed(3) + ')';\n        ctx.lineWidth = 1;\n        ctx.beginPath(); ctx.arc(n.x, n.y, ring, 0, 6.2832); ctx.stroke();\n      }\n    }\n\n    raf = requestAnimationFrame(frame);\n  }\n\n  function start(){ if(running) return; running = true; last = performance.now(); raf = requestAnimationFrame(frame); }\n  function stop(){ running = false; cancelAnimationFrame(raf); }\n\n  \/* Pointer tracking, scoped to the hero *\/\n  hero.addEventListener('pointermove', function(e){\n    if(e.pointerType === 'touch') return;\n    var r = hero.getBoundingClientRect();\n    ptr.x = e.clientX - r.left; ptr.y = e.clientY - r.top; ptr.idle = 0;\n  }, { passive:true });\n  hero.addEventListener('pointerleave', function(){ ptr.idle = 9; }, { passive:true });\n\n  \/* Rebuild on resize, pause when scrolled past *\/\n  var rt;\n  window.addEventListener('resize', function(){ clearTimeout(rt); rt = setTimeout(build, 220); });\n  if('ResizeObserver' in window) new ResizeObserver(function(){ clearTimeout(rt); rt = setTimeout(build, 220); }).observe(hero);\n  if('IntersectionObserver' in window){\n    new IntersectionObserver(function(en){ en[0].isIntersecting ? start() : stop(); }, { threshold:0 }).observe(hero);\n  }\n  document.addEventListener('visibilitychange', function(){ document.hidden ? stop() : start(); });\n\n  build();\n  start();\n  requestAnimationFrame(function(){ canvas.classList.add('is-live'); });\n})();\n<\/script>\n\n<script>\n\/* \u2500\u2500 Menu \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n(function(){\n  var toggle = document.getElementById('navToggle');\n  var menu   = document.getElementById('menu');\n  var cb     = document.getElementById('navToggleCb');\n  if(!toggle || !menu || !cb) return;\n\n  \/* The checkbox is the single source of truth. CSS opens the panel on its\n     own; this only mirrors the state for screen readers and scroll-lock. *\/\n  function setMenu(open){\n    cb.checked = open;\n    toggle.setAttribute('aria-expanded', open);\n    toggle.setAttribute('aria-label', open ? 'Close menu' : 'Open menu');\n    menu.classList.toggle('is-open', open);\n    document.body.classList.toggle('menu-open', open);\n  }\n\n  cb.addEventListener('change', function(){ setMenu(cb.checked); });\n\n  \/* Keyboard: a label does not fire on Enter or Space by itself. *\/\n  toggle.addEventListener('keydown', function(e){\n    if(e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar'){\n      e.preventDefault();\n      setMenu(!cb.checked);\n    }\n  });\n\n  \/* Accordion inside the panel *\/\n  menu.querySelectorAll('.menu__acc').forEach(function(btn){\n    var panel = document.getElementById(btn.getAttribute('aria-controls'));\n    btn.addEventListener('click', function(){\n      var open = btn.getAttribute('aria-expanded') === 'true';\n      btn.setAttribute('aria-expanded', !open);\n      panel.style.maxHeight = open ? null : panel.scrollHeight + 'px';\n    });\n  });\n\n  \/* Close on navigation, on Escape, and when resizing up to desktop *\/\n  menu.addEventListener('click', function(e){\n    if(e.target.closest('a')) setMenu(false);\n  });\n  document.addEventListener('keydown', function(e){\n    if(e.key === 'Escape' && menu.classList.contains('is-open')){\n      setMenu(false); toggle.focus();\n    }\n  });\n  window.addEventListener('resize', function(){\n    if(window.innerWidth > 1023 && menu.classList.contains('is-open')) setMenu(false);\n  });\n})();\n<\/script>\n\n<script>\n\/* \u2500\u2500 Job photos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   If a photo or the video is missing from the server, hide that tile rather\n   than showing a broken-image icon. If every tile fails, the whole strip goes.\n   Required files, all sitting beside index.html, names exactly as written:\n   IMG_0346.mp4, IMG_0346-poster.jpg, IMG_0037.jpg, IMG_0347.jpg, IMG_0497.jpg *\/\n(function(){\n  var strip = document.querySelector('.shots');\n  if(!strip) return;\n\n  function drop(el){\n    var tile = el.closest('.shot');\n    if(tile) tile.style.display = 'none';\n    if(!strip.querySelector('.shot:not([style*=\"none\"])')) strip.style.display = 'none';\n  }\n\n  strip.querySelectorAll('img').forEach(function(img){\n    img.addEventListener('error', function(){ drop(img); });\n    if(img.complete && img.naturalWidth === 0) drop(img);\n  });\n\n  strip.querySelectorAll('video').forEach(function(v){\n    v.addEventListener('error', function(){ drop(v); }, true);\n    v.addEventListener('loadeddata', function(){ v.dataset.ok = '1'; });\n    setTimeout(function(){ if(!v.dataset.ok && v.readyState === 0) drop(v); }, 6000);\n  });\n})();\n<\/script>\n\n<script>\n(function(){\n  \/* Quote form *\/\n  var form = document.getElementById('quoteForm'),\n      btn  = document.getElementById('submitBtn'),\n      ok   = document.getElementById('formSuccess'),\n      err  = document.getElementById('formError'),\n      mail = document.getElementById('formMailto');\n  if(!form) return;\n\n  var INBOX = 'info@onexmechanical.ca';\n\n  \/* True until a real Formspree (or other) endpoint is pasted into the\n     form's action attribute. Until then, every submit goes to email. *\/\n  function notConfigured(){\n    return \/YOUR_FORM_ID\/.test(form.action) || form.action.indexOf('http') !== 0;\n  }\n\n  function summary(){\n    var fd = new FormData(form), lines = [];\n    fd.forEach(function(v, k){\n      if(k.charAt(0) === '_' || !String(v).trim()) return;\n      lines.push(k.replace(\/_\/g, ' ') + ': ' + v);\n    });\n    return lines.join('\\n');\n  }\n\n  function emailLink(){\n    return 'mailto:' + INBOX +\n      '?subject=' + encodeURIComponent('Quote request from onexmechanical.ca') +\n      '&body=' + encodeURIComponent(summary());\n  }\n\n  function fail(){\n    if(mail) mail.href = emailLink();\n    err.style.display = 'block';\n    err.scrollIntoView({ behavior:'smooth', block:'center' });\n    btn.disabled = false; btn.textContent = 'Get my free quote';\n  }\n\n  form.addEventListener('submit', function(e){\n    e.preventDefault();\n    err.style.display = 'none';\n\n    if(notConfigured()){ fail(); return; }\n\n    btn.disabled = true; btn.textContent = 'Sending\\u2026';\n\n    fetch(form.action, { method:'POST', body:new FormData(form), headers:{ 'Accept':'application\/json' } })\n      .then(function(r){\n        if(!r.ok) throw new Error('bad');\n        form.style.display = 'none';\n        ok.style.display = 'block';\n        ok.scrollIntoView({ behavior:'smooth', block:'center' });\n      })\n      .catch(fail);\n  });\n})();\n<\/script>\n\n<script type=\"application\/ld+json\">\n{\n  \"@context\": \"https:\/\/schema.org\",\n  \"@type\": \"Plumber\",\n  \"@id\": \"https:\/\/onexmechanical.ca\/#business\",\n  \"name\": \"Onex Plumbing\",\n  \"description\": \"Licensed plumbing, drainage, heating and cooling for homes, stratas and commercial buildings across Nanaimo and Vancouver Island. 24\/7 emergency service.\",\n  \"url\": \"https:\/\/onexmechanical.ca\/\",\n  \"telephone\": \"+1-604-338-8408\",\n  \"email\": \"info@onexmechanical.ca\",\n  \"priceRange\": \"$$\",\n  \"address\": { \"@type\": \"PostalAddress\", \"addressLocality\": \"Nanaimo\", \"addressRegion\": \"BC\", \"postalCode\": \"\", \"addressCountry\": \"CA\" },\n  \"areaServed\": [\n    { \"@type\": \"City\", \"name\": \"Nanaimo\" },\n    { \"@type\": \"City\", \"name\": \"Lantzville\" },\n    { \"@type\": \"City\", \"name\": \"Nanoose Bay\" },\n    { \"@type\": \"City\", \"name\": \"Parksville\" },\n    { \"@type\": \"City\", \"name\": \"Qualicum Beach\" },\n    { \"@type\": \"City\", \"name\": \"Ladysmith\" },\n    { \"@type\": \"City\", \"name\": \"Chemainus\" },\n    { \"@type\": \"City\", \"name\": \"Duncan\" },\n    { \"@type\": \"City\", \"name\": \"Cedar\" },\n    { \"@type\": \"Place\", \"name\": \"Gabriola Island\" },\n    { \"@type\": \"City\", \"name\": \"Port Alberni\" },\n    { \"@type\": \"City\", \"name\": \"Courtenay\" },\n    { \"@type\": \"City\", \"name\": \"Comox\" }\n  ],\n  \"openingHoursSpecification\": [{\n    \"@type\": \"OpeningHoursSpecification\",\n    \"dayOfWeek\": [\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\",\"Sunday\"],\n    \"opens\": \"00:00\", \"closes\": \"23:59\"\n  }],\n  \"hasOfferCatalog\": {\n    \"@type\": \"OfferCatalog\", \"name\": \"Plumbing, heating and drainage services\",\n    \"itemListElement\": [\n      { \"@type\":\"Offer\",\"itemOffered\":{ \"@type\":\"Service\",\"name\":\"Emergency plumbing\" } },\n      { \"@type\":\"Offer\",\"itemOffered\":{ \"@type\":\"Service\",\"name\":\"Drain cleaning and sewer repair\" } },\n      { \"@type\":\"Offer\",\"itemOffered\":{ \"@type\":\"Service\",\"name\":\"Hot water tank installation and repair\" } },\n      { \"@type\":\"Offer\",\"itemOffered\":{ \"@type\":\"Service\",\"name\":\"Furnace and boiler service\" } },\n      { \"@type\":\"Offer\",\"itemOffered\":{ \"@type\":\"Service\",\"name\":\"Heat pump and air conditioning\" } },\n      { \"@type\":\"Offer\",\"itemOffered\":{ \"@type\":\"Service\",\"name\":\"Gas fitting\" } },\n      { \"@type\":\"Offer\",\"itemOffered\":{ \"@type\":\"Service\",\"name\":\"Renovation and new construction plumbing\" } },\n      { \"@type\":\"Offer\",\"itemOffered\":{ \"@type\":\"Service\",\"name\":\"Strata and commercial maintenance contracts\" } }\n    ]\n  }\n}\n<\/script>\n\n<script type=\"application\/ld+json\">\n{\n  \"@context\":\"https:\/\/schema.org\",\"@type\":\"FAQPage\",\"mainEntity\":[\n  {\"@type\":\"Question\",\"name\":\"Do you offer 24\/7 emergency plumbing in Nanaimo?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes. We take emergency calls around the clock across Nanaimo and mid-Vancouver Island, for homes, stratas and commercial buildings.\"}},\n  {\"@type\":\"Question\",\"name\":\"How much does a plumber cost?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"It depends on the job, which is why we quote before starting. You get one written price up front and that is what you are invoiced.\"}},\n  {\"@type\":\"Question\",\"name\":\"Are you licensed and insured?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes. Licensed through Skilled Trades BC, fully insured, and WorkSafeBC compliant. Certificates available on request.\"}},\n  {\"@type\":\"Question\",\"name\":\"Do you work on both houses and commercial buildings?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Both. Single-family homes, condos, rentals, strata buildings, retail units, schools and service stations.\"}},\n  {\"@type\":\"Question\",\"name\":\"Can you replace a hot water tank the same day?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Usually yes. Tank, tankless and heat-pump water heaters from all major brands, with same-day replacement available in most cases.\"}},\n  {\"@type\":\"Question\",\"name\":\"How far do you travel?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Ladysmith through Qualicum Beach is standard coverage, including Gabriola Island, and further up-Island for project work.\"}},\n  {\"@type\":\"Question\",\"name\":\"Do you take on strata and property management contracts?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes. Scheduled maintenance and priority dispatch for strata corporations and property managers, with consistent reporting across the portfolio.\"}}\n]}\n<\/script>\n\n","protected":false},"excerpt":{"rendered":"<p>OnexPlumbing About Residential Commercial &amp; strata Services Drain cleaning &amp; camera Hot water tanks Heating &amp; heat pumps Sewer &amp; underground Repairs &amp; renovations Service areas Free quote 24\/7 emergency (604) 338-8408 Home About Residential Commercial &amp; strata Services Drain cleaning &amp; camera Hot water tanks Heating &amp; heat pumps Sewer &amp; underground Repairs &amp; [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-73","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/onexmechanical.ca\/index.php?rest_route=\/wp\/v2\/pages\/73","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/onexmechanical.ca\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/onexmechanical.ca\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/onexmechanical.ca\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/onexmechanical.ca\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=73"}],"version-history":[{"count":1,"href":"https:\/\/onexmechanical.ca\/index.php?rest_route=\/wp\/v2\/pages\/73\/revisions"}],"predecessor-version":[{"id":74,"href":"https:\/\/onexmechanical.ca\/index.php?rest_route=\/wp\/v2\/pages\/73\/revisions\/74"}],"wp:attachment":[{"href":"https:\/\/onexmechanical.ca\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=73"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}