/* Glow Electrical Contracting — concept 4, "Current".
   Built off their NEW brand identity, submitted 13 Aug 2026, not the old gold
   Wix look concepts 1 to 3 are built on. Client's own words: "Sharp. Trusted.
   Current." Dark, minimal, technically confident, premium. Reference sites
   they named: westcork.com.au, tesla.com/en_au, newageelectricalco.com.au,
   havenelectrical.com.au.

   Palette, sampled not guessed:
     #2D6CA3  the plug in ring, sampled straight off their PDF logo pack
     #545454  their own stated grey
     Black + white, per their own brand colour list.

   NOTE (18 Aug 2026): the van's vivid #0082DD was previously used here as a
   decorative glow. It has been REMOVED entirely — glowing blue accents are
   22 Electrical Works' signature and were the single biggest reason the two
   client sites read as the same template. See the palette block below. */

/* ---------------------------------------------------------------------------
   PALETTE, revised 18 Aug 2026 — deliberate separation from 22 Electrical Works.
   Josh at 22EW is also a Scalus client and also an electrician in Melbourne's
   east, and side by side the two sites read as one template. 22EW's ground is
   #0B0E14 / #080B10, a COLD BLUE BLACK (hue ~220), its accent is #00A2E2 cyan
   used as a glow on almost every element, and its text is #E8EBF0, a cool grey.
   Glow now moves the opposite way on the colour wheel: a WARM GRAPHITE ground
   (hue ~25, a black carrying their own #545454 grey plus a trace of warmth)
   and a WARM PAPER text colour, with #2D6CA3 demoted to a STRUCTURAL colour —
   rules, borders, hairlines, small type accents — never a glow. Every
   rgba(45,108,163,...) box-shadow and radial "glow" in this file was removed
   in the same pass. The two grounds are now ~40 degrees of hue and opposite
   temperatures apart, which is the difference you can see at a glance.

   Contrast, computed not eyeballed, with every translucent layer composited
   down onto the ground behind it first:
     --fg on --ink 16.75:1, on --ink-2 15.67, on --ink-3 14.17
     --fg-mute (72%) 8.96 / 8.57
     --fg-mute2 (55%) 5.51 on --ink-2, 4.88 on the deepest ground --ink-4
     --blue-dk #709bc0 as text: 6.35 / 5.94 / 5.37 / 4.70 across the four
       grounds, so it passes 4.5:1 everywhere it is used, including on
       --ink-4 which the old #6291BA failed at 4.14.
     white on raw --blue 5.55, so buttons are unchanged.
   --------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------
   THEME LAYER, added 18 Aug 2026 — light and dark, defaulting to the visitor.
   Tim: "the site needs to have light and dark modes, and make it default to the
   user ... then have the option for light and dark ... on bottom left."

   How it works, and why it is built this way:

   1. A tiny inline script in <head> stamps data-theme on <html> BEFORE first
      paint, reading localStorage first and prefers-color-scheme second. Setting
      it after paint is the classic failure here: the page renders in the wrong
      theme for a frame and reads as broken.
   2. Everything below is a TOKEN swap on that one attribute. No duplicated
      stylesheet, no per-component light variants — the components were already
      written against tokens, so the whole site follows automatically.
   3. The bare :root selector carries the dark values as well, so a visitor with
      JavaScript off still gets a complete, correct dark site rather than an
      unstyled one.

   The light theme is deliberately NOT a generic white template. It keeps the
   same warm axis the dark theme is built on: a warm paper ground (hue ~35) and
   a warm near-black ink, so it reads as the same brand with the light switched
   on. 22EW has no light mode at all, and the warm/cold separation that keeps
   the two apart is carried by both of Glow's themes.
   --------------------------------------------------------------------------- */
:root{
  --blue:#2D6CA3;         /* the plug in ring, sampled off their PDF logo pack */
  --blue-lt:#3A7AB8;      /* their own stated upper end of the blue range */
  --on-blue:#ffffff;
  --grey:#545454;
  --gold:#E0A32E;         /* the review stars, see the star block below */
  --gold-glow:224,163,46;
}

/* ---------------------------------------------------------------------------
   DARK — the ground the brand was designed on, and still the default when the
   visitor has no stored choice and no system preference.
   --------------------------------------------------------------------------- */
:root,
:root[data-theme="dark"]{
  color-scheme:dark;
  --blue-dk:#709bc0;      /* on-dark text safe on all four grounds, >=4.70:1 */
  --blue-line:rgba(45,108,163,.42);  /* structural rules and borders */
  /* warm graphite ground — their grey warmed, NOT 22EW's blue black */
  --ink:#141210;
  --ink-2:#1C1917;
  --ink-3:#252220;
  --ink-4:#302C29;
  --ink-rgb:20,18,16;
  --fg:#F5F2EE;           /* warm paper, not 22EW's cool #E8EBF0 */
  --fg-rgb:245,242,238;
  --fg-mute:rgba(245,242,238,.72);
  --fg-mute2:rgba(245,242,238,.55);
  --line:rgba(245,242,238,.10);
  --line-2:rgba(245,242,238,.18);
  --tint-1:rgba(245,242,238,.045);   /* a barely-there raise off the ground */
  --tint-2:rgba(245,242,238,.028);
  --mesh:rgba(245,242,238,.05);      /* the hero texture and hex divider lines */
  --ft-bg:#08090a;
  --scrim:rgba(12,13,15,.75);        /* photo tags, pinned bar, chat panel */
  --glass-bg:rgba(21,23,26,.55);
  --glass-hi:rgba(255,255,255,.09);
  --glass-hi-2:rgba(255,255,255,.16);
  --glass-line:rgba(255,255,255,.14);
  --sh:0 24px 60px rgba(16,12,8,.5);
  /* the lockup is white on every placement now — see the note on
     .sec-invert .form-brand. Nothing reads these two any more (the artwork is
     resolved in the markup at build time), but leaving them pointing at the
     dark files would hand the next person the wrong answer. */
  --logo-h:/media/brand/logo-horiz-white.png;
  --area-map:url(/media/service-area.svg);
  --area-map-sm:url(/media/service-area-sm.svg);
  --logo-s:/media/brand/logo-stacked-white.png;
  --theme-icon-sun:1;
  --theme-icon-moon:0;
}

/* ---------------------------------------------------------------------------
   LIGHT — warm paper, warm ink. Contrast, computed with every translucent
   layer composited down onto what sits behind it, never read off the raw rgba:
     --fg #171412 on --ink #F6F3EE      16.02 : 1
     --fg on --ink-2 #EFEAE2            15.02 : 1
     --fg-mute (74%) on --ink            9.28 : 1
     --fg-mute2 (58%) on --ink           5.67 : 1, on --ink-3 #E6DFD4  5.19 : 1
     --blue-dk #1F5A8C on --ink          6.31 : 1, on --ink-3         5.77 : 1
     white on --blue #2D6CA3             5.55 : 1  (buttons unchanged)
   --blue-dk had to be DARKENED for this theme rather than reused: the dark
   theme's #709bc0 lands at 2.4:1 on warm paper, which is the single most
   common way a theme swap ships an unreadable link colour.
   --------------------------------------------------------------------------- */
:root[data-theme="light"]{
  color-scheme:light;
  --blue-dk:#1F5A8C;
  --blue-line:rgba(45,108,163,.34);
  --ink:#F6F3EE;
  --ink-2:#EFEAE2;
  --ink-3:#E6DFD4;
  --ink-4:#DBD3C6;
  --ink-rgb:246,243,238;
  --fg:#171412;
  --fg-rgb:23,20,18;
  --fg-mute:rgba(23,20,18,.74);
  --fg-mute2:rgba(23,20,18,.64);   /* 58% measured 4.10 on --ink-3 and in the footer */
  --line:rgba(23,20,18,.12);
  --line-2:rgba(23,20,18,.22);
  --tint-1:rgba(23,20,18,.045);
  --tint-2:rgba(23,20,18,.03);
  --mesh:rgba(23,20,18,.05);
  --ft-bg:#E7E0D6;
  --scrim:rgba(246,243,238,.82);
  --glass-bg:rgba(255,255,255,.55);
  --glass-hi:rgba(255,255,255,.55);
  --glass-hi-2:rgba(255,255,255,.75);
  --glass-line:rgba(23,20,18,.14);
  --sh:0 24px 60px rgba(60,44,28,.14);
  --logo-h:/media/brand/logo-horiz-light.png;
  --area-map:url(/media/service-area-light.svg);
  --area-map-sm:url(/media/service-area-light-sm.svg);
  --logo-s:/media/brand/logo-stacked-light.png;
  --theme-icon-sun:0;
  --theme-icon-moon:1;
}

:root{
  /* Type, chosen by rendering "GLOW" beside the actual logo file in six
     Fontshare display candidates (Supreme, Nippo, Excon, Technor, Panchang,
     Chillax) at matched size and picking by eye, not by name. Supreme's W
     has the same pointed apex as the logo's W, its O is the same wide near
     perfect circle as the plug ring, and its G is a plain open geometric
     form like the mark's, where Excon's G carried a stylised terminal spur
     the logo doesn't have and Technor's cut corners fought the logo's clean
     even strokes. Switzer pairs as the quiet workhorse: a neutral geometric
     grotesk built for exactly this loud/quiet split (see the Scalus type
     library's own "Clash Display + Switzer" trades pairing), legible small,
     and nowhere near the banned Inter/Space Grotesk default set. */
  --disp:"Supreme",system-ui,sans-serif;
  --text:"Switzer",system-ui,-apple-system,"Segoe UI",sans-serif;
  /* Editorial / spec-sheet layout language. 22EW is a 1240px wrap with 16px
     radius pill-button cards on a 104px section rhythm — a conventional
     stacked marketing site. Glow goes wider and squarer: a 1320px measure so
     the type can run big and the compositions can go asymmetric, a 3px radius
     that reads as a drawn rule rather than a rounded card, and a warm shadow
     instead of a black one. Michael's own references are Tesla and West Cork. */
  --wrap:1320px;
  /* Tim, 18 Aug: "have rounded corners" on the work cards. 10px, not 16px.
     22EW's signature is a 16px pill-radius card that lifts and glows on hover,
     and the sharp 3px was part of deliberately not being that. 10px reads as
     intentionally rounded while staying visibly tighter than theirs, and the
     rest of the separation is structural rather than cosmetic: Glow keeps the
     hairline-ruled numbered spec-sheet sections and the no-lift card hover.
     Applied through the shared token so it lands on every card at once, not
     just the three Tim was looking at. */
  --r:10px;
  --ease:cubic-bezier(.22,1,.36,1);
  --mono:"Supreme",ui-monospace,SFMono-Regular,Menlo,monospace;
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--ink);color:var(--fg-mute);
  font-family:var(--text);font-size:17px;line-height:1.66;font-weight:400;
  overflow-x:hidden;-webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block;height:auto}
a{color:inherit}
h1,h2,h3,h4{font-family:var(--disp);font-weight:600;color:var(--fg);margin:0;
  line-height:1.06;letter-spacing:-.01em}
p{margin:0 0 1em}
.wrap{width:min(100% - 40px,var(--wrap));margin-inline:auto}
section[id],article[id],#hero-quiz{scroll-margin-top:96px}
@media(max-width:640px){section[id],article[id],#hero-quiz{scroll-margin-top:86px}}

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.55em;
  font-family:var(--disp);font-weight:600;font-size:14.5px;letter-spacing:.06em;
  text-transform:uppercase;text-decoration:none;cursor:pointer;
  padding:16px 28px;border-radius:var(--r);border:1.5px solid transparent;
  min-height:54px;transition:transform .2s ease,background .2s ease,color .2s ease,box-shadow .2s ease,border-color .2s ease;
}
.btn:hover{transform:translateY(-2px)}
.btn-pri{background:var(--blue);color:var(--on-blue)}
.btn-pri:hover{background:var(--blue-lt)}
.btn-gho{border-color:var(--line-2);color:var(--fg)}
.btn-gho:hover{border-color:var(--blue-dk);color:var(--blue-dk)}
.btn-ink{background:var(--fg);color:var(--ink)}
.btn-ink:hover{background:#fff}
.btn-sm{padding:11px 19px;min-height:44px;font-size:13px}
.btn[disabled]{background:var(--ink-3);color:var(--fg-mute2);box-shadow:none;transform:none;cursor:not-allowed}

/* ---------- header ---------- */
.hd{position:sticky;top:0;z-index:60;background:rgba(var(--ink-rgb),.86);
  backdrop-filter:blur(16px) saturate(140%);border-bottom:1px solid var(--line)}
.hd-in{display:flex;align-items:stretch;gap:22px;min-height:76px}
.brand{display:flex;align-items:center;text-decoration:none;flex:0 0 auto}
.brand img{height:32px;width:auto}

/* ---------------------------------------------------------------------------
   NAV ALIGNMENT (Tim, 18 Aug: "make sure these all line up properly").
   The old rule hung a `border-bottom` off each <a> while the row was
   `align-items:center`, so the underline sat at the bottom of each link's own
   box rather than on one shared baseline, and the boxes were different heights
   because some items are links and one is a <button>. Fixed by making every
   nav item a full-height flex cell in a stretched row, centring the label
   inside it, and drawing the indicator as an absolutely positioned 2px rule
   pinned to the BOTTOM OF THE HEADER — one line, one y position, identical for
   hover, focus and the active page, whatever the item is.
   --------------------------------------------------------------------------- */
.nav{display:flex;gap:4px;margin-left:auto;align-items:stretch}
.nav>*{position:relative;display:flex;align-items:center}
.nav a,.nav .navtop{font-family:var(--disp);font-weight:500;font-size:14px;
  letter-spacing:.03em;line-height:1;color:var(--fg-mute);text-decoration:none;
  display:flex;align-items:center;gap:7px;
  padding:0 14px;height:100%;background:none;border:0;cursor:pointer;
  font-family:var(--disp);transition:color .2s}
.nav>*::after{content:"";position:absolute;left:14px;right:14px;bottom:-1px;height:2px;
  background:var(--blue-dk);transform:scaleX(0);transform-origin:left;
  transition:transform .28s var(--ease)}
.nav>*:hover::after,.nav>*:focus-within::after{transform:none}
.nav a:hover,.nav .navtop:hover{color:var(--fg)}
.nav a[aria-current="page"]{color:var(--fg)}
.nav>.is-active::after{transform:none;background:var(--blue)}

/* --- dropdowns (Tim, 18 Aug: Services and Areas list their child pages) ---
   Hover OR focus-within on desktop opens it, and the trigger is a real
   <button aria-expanded> so a keyboard user can Enter/Space it and Escape out
   — g4.js drives that. On touch the first tap opens the panel instead of
   following the link, which is why the trigger is a button and the index page
   gets its own explicit row inside the panel. Below the burger breakpoint the
   whole thing is replaced by the accordion inside .mnav, so nothing here
   depends on hover on a phone. */
.navdd{position:relative}
.navdd .navtop svg{transition:transform .25s var(--ease);opacity:.7}
.navdd[data-open="true"] .navtop svg{transform:rotate(90deg)}
.navpanel{position:absolute;top:100%;left:0;min-width:288px;
  background:var(--ink-2);border:1px solid var(--line-2);border-top:2px solid var(--blue);
  padding:8px 0;box-shadow:var(--sh);
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity .2s ease,transform .2s var(--ease),visibility .2s;z-index:5}
.navdd:hover .navpanel,.navdd:focus-within .navpanel,.navdd[data-open="true"] .navpanel{
  opacity:1;visibility:visible;transform:none}
.navpanel a{display:flex;align-items:center;justify-content:space-between;gap:14px;
  height:auto;padding:11px 18px;font-family:var(--text);font-size:14.5px;
  font-weight:500;letter-spacing:0;color:var(--fg-mute);white-space:nowrap;
  border-left:2px solid transparent;transition:color .18s,border-color .18s,background .18s}
.navpanel a:hover,.navpanel a:focus-visible{color:var(--fg);background:var(--tint-1);
  border-left-color:var(--blue)}
.navpanel a em{font-style:normal;font-size:11.5px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--fg-mute2);font-family:var(--disp)}
.navpanel .np-all{border-top:1px solid var(--line);margin-top:6px;padding-top:13px;
  color:var(--blue-dk);font-weight:600}
.hd-cta{flex:0 0 auto;align-self:center}
.burger{display:none;width:48px;height:48px;border:0;background:transparent;padding:0;
  cursor:pointer;margin-left:auto;position:relative}
.burger span,.burger::before,.burger::after{content:"";position:absolute;left:11px;right:11px;
  height:2px;background:var(--fg);border-radius:2px;transition:transform .25s ease,opacity .2s ease}
.burger span{top:23px}.burger::before{top:15px}.burger::after{top:31px}
.burger[aria-expanded="true"] span{opacity:0}
.burger[aria-expanded="true"]::before{transform:translateY(8px) rotate(45deg)}
.burger[aria-expanded="true"]::after{transform:translateY(-8px) rotate(-45deg)}
.mnav{display:none;position:fixed;left:0;right:0;background:var(--ink-2);
  border-top:1px solid var(--line);padding:10px 20px 22px;z-index:49;
  max-height:calc(100vh - 76px);overflow:auto}
.mnav a{display:block;padding:16px 4px;min-height:54px;color:var(--fg);text-decoration:none;
  font-family:var(--disp);font-size:19px;letter-spacing:.01em;
  border-bottom:1px solid var(--line)}
.mnav>a:last-child{border-bottom:0;color:var(--blue-dk)}
.mnav.on{display:block}

/* --- mobile dropdowns expand INSIDE the burger menu, never on hover --------
   Tim, 18 Aug: the Services and Areas dropdowns must "expand inside the burger
   menu rather than relying on hover". Each group is a row containing the index
   link plus a separate 54px chevron button that toggles the child list. The
   two are separate targets on purpose, so tapping "Services" still goes to the
   services index and only the chevron opens the list — no trapped nav item. */
.mgrp{border-bottom:1px solid var(--line)}
.mgrp-top{display:flex;align-items:stretch;gap:0}
.mgrp-top>a{flex:1 1 auto;border-bottom:0}
.mgrp-tog{flex:0 0 auto;width:56px;min-height:54px;background:none;border:0;
  border-left:1px solid var(--line);color:var(--fg-mute);cursor:pointer;
  display:grid;place-items:center;padding:0}
.mgrp-tog svg{transition:transform .25s var(--ease)}
.mgrp-tog[aria-expanded="true"]{color:var(--blue-dk)}
.mgrp-tog[aria-expanded="true"] svg{transform:rotate(90deg)}
.mgrp-list{display:none;padding:2px 0 12px;border-top:1px solid var(--line)}
.mgrp-list.on{display:block}
.mgrp-list a{padding:13px 4px 13px 18px;min-height:48px;font-family:var(--text);
  font-size:16px;font-weight:500;color:var(--fg-mute);border-bottom:0;
  border-left:2px solid var(--line);margin-left:2px}
.mgrp-list a:hover,.mgrp-list a:focus-visible{color:var(--fg);border-left-color:var(--blue)}
.mgrp-list a.np-all{color:var(--blue-dk);font-weight:600}
@media(max-width:1040px){
  .nav{display:none}.hd-cta{display:none}.burger{display:block}
}

/* ---------- hero ---------- */
.hero{position:relative;background:var(--ink);color:var(--fg);overflow:hidden;
  padding:80px 0 92px;isolation:isolate}
/* decorative hex/dot texture off the van wrap's panel treatment. Named clear
   of "grid", "inner", "container" and "wrap" so it can never be mistaken for
   the hero quiz's fallback host wrapper or the right-hand visual. */
.hero-texture{position:absolute;inset:0;z-index:-2;opacity:.5;pointer-events:none;
  background-image:linear-gradient(var(--mesh) 1px,transparent 1px),
    linear-gradient(90deg,var(--mesh) 1px,transparent 1px);
  background-size:96px 96px;
  mask-image:radial-gradient(ellipse 90% 70% at 78% 30%, #000 0%, transparent 72%);
  -webkit-mask-image:radial-gradient(ellipse 90% 70% at 78% 30%, #000 0%, transparent 72%);
}
/* the van wrap's diagonal chevron cut, echoed as a soft light sweep rather than reproduced */
.hero-chevron{position:absolute;z-index:-1;inset:-10% -20% -10% 38%;pointer-events:none;
  background:linear-gradient(115deg, transparent 42%, var(--tint-1) 43%, var(--tint-2) 52%, transparent 53%);
}
.hero::after{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(180deg,rgba(var(--ink-rgb),.35) 0%,transparent 30%,rgba(var(--ink-rgb),.55) 88%,var(--ink) 100%)}
.hero-in{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  gap:52px;align-items:center}
/* Tim, 20 Aug: "remove ALL HYPHENS EVERYWHERE", pointing at the short rule
   that used to sit in front of this eyebrow. It was a decorative 26x1px bar
   rather than a character, but on screen it read as a dash in front of the
   words, which is the thing the rule is about. Removed outright rather than
   swapped for a dot, so there is nothing left in front of the text to read as
   punctuation. The pulse and glow that were attached to it went with it. */
.eyebrow{font-family:var(--disp);font-size:13px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--blue-dk);margin:0 0 16px;font-weight:600;display:flex;align-items:center;gap:9px}
.hh{font-size:clamp(34px,5.3vw,60px);color:var(--fg);margin:0 0 20px;letter-spacing:-.01em}
.hh em{font-style:normal;color:var(--blue-dk)}
.hsub{font-size:clamp(16px,1.5vw,18.5px);color:var(--fg-mute);max-width:36em;margin:0 0 30px}
.hcta{display:flex;flex-wrap:wrap;gap:14px;margin-bottom:32px}
.hchips{display:flex;flex-wrap:wrap;gap:10px;list-style:none;padding:0;margin:0}
.hchips li{display:flex;align-items:center;gap:9px;font-size:13px;font-weight:600;
  color:var(--fg-mute);background:var(--tint-1);border:1px solid var(--line);
  border-radius:100px;padding:9px 16px}
.hchips svg{flex:0 0 auto;color:var(--blue-dk)}

/* the right hand hero visual — a "system snapshot" technical panel. Static
   position, real size, non decorative name, so the hero quiz finds and
   replaces it cleanly on desktop. */
.hero-fig{border-radius:var(--r);overflow:hidden;min-height:340px;
  background:var(--ink-2);border:1px solid var(--line);position:relative}
.hero-fig .hf-head{display:flex;align-items:center;justify-content:space-between;
  padding:20px 22px;border-bottom:1px solid var(--line)}
.hero-fig .hf-head b{font-family:var(--disp);font-size:13px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-mute)}
.hf-dot{width:8px;height:8px;border-radius:50%;background:#3ccB78;box-shadow:0 0 0 4px rgba(60,203,120,.18)}
.hf-rows{display:grid;gap:1px;background:var(--line)}
.hf-row{display:flex;align-items:center;gap:14px;padding:18px 22px;background:var(--ink-2)}
.hf-row svg{flex:0 0 auto;color:var(--blue-dk)}
.hf-row div{display:flex;flex-direction:column;gap:3px}
.hf-row b{font-size:14.5px;color:var(--fg);font-weight:600}
.hf-row span{font-size:12.5px;color:var(--fg-mute2)}

/* the pinned mobile action bar that pairs with the hero form */
.pinbar{position:fixed;left:0;right:0;bottom:0;z-index:70;display:none;gap:10px;
  padding:10px 14px calc(10px + env(safe-area-inset-bottom));
  background:var(--scrim);backdrop-filter:blur(22px) saturate(160%);
  border-top:1px solid var(--line-2);transform:translateY(110%);
  transition:transform .28s ease}
.pinbar.on{transform:none}
.pinbar .btn{flex:1 1 0;min-height:52px;font-size:13.5px;padding:14px 12px}
@media(max-width:900px){.pinbar{display:flex}}

/* ---------- accreditation strip ---------- */
.accs{background:var(--ink-2);border-bottom:1px solid var(--line);padding:28px 0}
.accs-in{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:20px 30px}
.accs p{margin:0 26px 0 0;font-size:12px;letter-spacing:.14em;text-transform:uppercase;
  font-family:var(--disp);color:var(--fg-mute2);font-weight:500;flex:0 0 auto}
.accs-badge{background:#fff;border-radius:8px;padding:10px 16px;display:flex;align-items:center}
.accs-badge img{height:34px;width:auto}
.accs-badge img.rec{height:30px}
/* Sarah, 2 Sep: REC licence number in the footer, and a brands we trust row
   under the accreditation badges. */
.accs-brands{margin-top:22px;gap:18px 44px}
/* Michael, 3 Sep: the brands row carries the real marks, white on the dark strip,
   not text pills. Each PNG is drawn on a 96px canvas with its glyph pre scaled, so
   one height here keeps all three optically level. */
.accs-brand{display:flex;align-items:center}
.accs-brand img{height:26px;width:auto;opacity:.82;
  transition:opacity .25s ease}
.accs-brand:hover img{opacity:1}
/* The marks are white greyscale with alpha, so the light theme simply inverts
   them to the warm near black rather than shipping a second set of files. */
:root[data-theme="light"] .accs-brand img{filter:invert(1);opacity:.78}
:root[data-theme="light"] .accs-brand:hover img{opacity:1}
@media(max-width:600px){.accs-brand img{height:22px}.accs-brands{gap:16px 30px}}
.ft-rec{margin-top:8px;font-size:13.5px;letter-spacing:.04em}

/* ---------- generic section ---------- */
.sec{padding:100px 0}
.sec-tint{background:var(--ink-2)}
.sec-dark{background:var(--ink-3)}
.shead{max-width:640px;margin:0 0 52px}
.shead.mid{margin-inline:auto;text-align:center}
.kicker{font-family:var(--disp);font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--blue-dk);font-weight:600;margin:0 0 14px}
.shead h2{font-size:clamp(28px,3.8vw,44px);margin:0 0 18px}
.shead p{font-size:17px;margin:0;color:var(--fg-mute)}
.rule{width:44px;height:3px;background:var(--blue);border-radius:2px;margin:0 0 22px}
.shead.mid .rule{margin-inline:auto}

/* ---------- story / why us ---------- */
.story{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.82fr);gap:60px;align-items:start}
.story p{font-size:17px;color:var(--fg-mute)}
.story-card{background:var(--ink-2);border:1px solid var(--line);border-radius:var(--r);padding:34px;
  clip-path:polygon(0 0,calc(100% - 26px) 0,100% 26px,100% 100%,0 100%)}
.story-card h3{font-size:22px;color:var(--fg);margin:0 0 18px}
.story-card ul{list-style:none;margin:0;padding:0;display:grid;gap:14px}
.story-card li{display:flex;gap:12px;align-items:flex-start;font-size:15px;
  color:var(--fg-mute);line-height:1.55}
.story-card svg{flex:0 0 auto;color:var(--blue-dk);margin-top:3px}

/* ---------- stats ---------- */
.stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1px;margin-top:64px;
  background:var(--line);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.stat{background:var(--ink);padding:30px 22px;text-align:center}
.stat b{display:block;font-family:var(--disp);font-size:clamp(30px,3.6vw,42px);
  color:var(--blue-dk);line-height:1;margin-bottom:10px}
.stat span{display:block;font-size:13px;letter-spacing:.02em;color:var(--fg-mute);font-weight:500}

/* ---------- services ---------- */
.svcs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:22px}
.svc{background:var(--ink);border:1px solid var(--line);border-radius:var(--r);
  padding:32px 28px 30px;position:relative;overflow:hidden;
  transition:transform .25s ease,box-shadow .25s ease,border-color .25s ease}
.sec-tint .svc{background:var(--ink-2)}
.svc::before{content:"";position:absolute;left:0;right:0;top:0;height:3px;
  background:var(--blue);transform:scaleX(0);transform-origin:left;transition:transform .3s ease}
.svc:hover{border-color:var(--blue-line)}
.svc:hover::before{transform:none}
.svc-ico{width:46px;height:46px;border-radius:9px;background:rgba(45,108,163,.16);
  display:grid;place-items:center;color:var(--blue-dk);margin-bottom:18px}
.svc-n{font-family:var(--disp);font-size:13px;letter-spacing:.12em;color:var(--fg-mute2);
  font-weight:600;display:block;margin-bottom:10px}
.svc h3{font-size:21px;margin:0 0 12px}
.svc p{font-size:15px;margin:0 0 18px;color:var(--fg-mute)}
.svc-price{display:flex;align-items:baseline;gap:8px;padding:14px 0;border-top:1px solid var(--line);
  margin-bottom:16px;flex-wrap:wrap}
.svc-price b{font-family:var(--disp);font-size:19px;color:var(--fg)}
.svc-price span{font-size:12px;color:var(--fg-mute2);letter-spacing:.03em}
.svc ul{list-style:none;margin:0;padding:0;display:grid;gap:9px}
.svc li{display:flex;gap:10px;align-items:flex-start;font-size:14px;line-height:1.5;color:var(--fg-mute)}
.svc li::before{content:"";flex:0 0 auto;width:6px;height:6px;
  background:var(--blue);margin-top:8px;transform:rotate(45deg)}
.price-note{margin:28px auto 0;max-width:700px;text-align:center;font-size:13.5px;
  color:var(--fg-mute2)}

/* ---------- technical confidence ---------- */
.techs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:22px}
.tech{padding:28px 26px;border:1px solid var(--line);border-radius:var(--r);background:var(--ink)}
.tech-ico{width:42px;height:42px;border-radius:8px;background:rgba(45,108,163,.16);
  display:grid;place-items:center;color:var(--blue-dk);margin-bottom:16px}
.tech h3{font-size:18px;margin:0 0 10px}
.tech p{font-size:14.5px;color:var(--fg-mute);margin:0}

/* ---------- testimonials ---------- */
.quotes{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:22px}
.quote{background:var(--ink-2);color:var(--fg-mute);border:1px solid var(--line);border-radius:var(--r);
  padding:32px 28px;position:relative;display:flex;flex-direction:column}
.quote p{font-size:15.5px;line-height:1.62;margin:0 0 22px;color:var(--fg)}
.quote b{font-family:var(--disp);font-size:14px;letter-spacing:.04em;
  margin-top:auto;color:var(--blue-dk);font-weight:600}
.quote svg{position:absolute;right:22px;bottom:20px;opacity:.3;color:var(--blue)}
.qnote{margin:26px 0 0;font-size:13px;color:var(--fg-mute2);text-align:center}

/* ---------- gallery placeholder ---------- */
.gal{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px}
.gal-i{aspect-ratio:4/3;border-radius:var(--r);border:1.5px dashed var(--line-2);
  background:
    repeating-linear-gradient(45deg,rgba(45,108,163,.07) 0 12px,transparent 12px 24px),
    var(--ink-2);
  display:grid;place-items:center;text-align:center;padding:22px}
.gal-i span{font-family:var(--disp);font-size:13px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--fg-mute2);font-weight:500}
.gal-note{margin:24px auto 0;max-width:700px;text-align:center;font-size:14px;
  background:rgba(45,108,163,.1);border:1px solid rgba(45,108,163,.3);
  border-radius:10px;padding:16px 20px;color:var(--fg-mute)}

/* ---------- service area ---------- */
.areas{display:flex;flex-wrap:wrap;gap:12px;list-style:none;padding:0;margin:0}
.areas li{font-family:var(--disp);font-size:14px;letter-spacing:.02em;
  border:1px solid var(--line-2);border-radius:100px;padding:11px 22px;color:var(--fg-mute)}
.areas li.more{border-style:dashed;color:var(--fg-mute2)}
.council{display:flex;flex-wrap:wrap;gap:14px;margin:0 0 30px}
.council span{background:rgba(45,108,163,.12);border:1px solid rgba(45,108,163,.32);
  color:var(--blue-dk);font-family:var(--disp);font-size:13px;font-weight:600;
  letter-spacing:.04em;padding:10px 18px;border-radius:100px}

/* ---------- contact ---------- */
.ct{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);gap:56px;align-items:start}
.ct-list{list-style:none;margin:26px 0 0;padding:0;display:grid;gap:18px}
.ct-list li{display:flex;gap:14px;align-items:flex-start}
.ct-list svg{flex:0 0 auto;color:var(--blue-dk);margin-top:3px}
.ct-list b{display:block;font-family:var(--disp);font-size:12.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--fg-mute2);font-weight:500;margin-bottom:2px}
.ct-list a{color:var(--fg);text-decoration:none;font-size:16.5px;font-weight:600;
  display:inline-block;padding:9px 0;min-height:40px;line-height:1.35;
  border-bottom:1px solid rgba(45,108,163,.4)}
.ct-list a:hover{color:var(--blue-dk)}
.form{background:var(--ink);border-radius:var(--r);overflow:hidden;box-shadow:var(--sh);
  border:1px solid var(--line)}
.form-brand{background:linear-gradient(180deg,rgba(45,108,163,.22),rgba(45,108,163,0));
  padding:24px;display:grid;place-items:center;border-bottom:1px solid var(--line)}
.form-brand img{height:64px;width:auto}
.form h3{font-size:22px;margin:0 0 8px;color:var(--fg)}
.form p,.form .f label{color:var(--fg-mute)}
.form .f label{color:var(--fg-mute2)}
.form>.form-in>p{font-size:14.5px;margin:0 0 22px}
.form-in{padding:30px 30px 32px}
.fgrid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.f{display:flex;flex-direction:column;gap:7px}
.f.full{grid-column:1/-1}
.f label{font-size:12px;letter-spacing:.1em;text-transform:uppercase;font-weight:600;
  color:var(--fg-mute2);font-family:var(--disp)}
.f input,.f select,.f textarea{
  font:inherit;font-size:16px;color:var(--fg);background:var(--ink-2);
  border:1.5px solid var(--line);border-radius:6px;padding:14px 15px;min-height:52px;
  width:100%;transition:border-color .2s,background .2s}
.f textarea{min-height:112px;resize:vertical}
.f input:focus,.f select:focus,.f textarea:focus{outline:0;border-color:var(--blue);
  background:var(--ink);box-shadow:0 0 0 3px rgba(45,108,163,.28)}
.form .btn{width:100%;margin-top:20px}
.fnote{font-size:12px;color:var(--fg-mute2);margin:14px 0 0;text-align:center}
.fdone{margin:16px 0 0;padding:14px 16px;border-radius:9px;background:rgba(45,108,163,.14);
  border:1px solid rgba(45,108,163,.4);font-size:14px;color:var(--fg);display:none}
.fdone.on{display:block}

/* ---------- footer ---------- */
.ft{background:var(--ft-bg);color:var(--fg-mute2);padding:64px 0 0;font-size:14.5px;
  border-top:1px solid var(--line)}
.ft-in{display:grid;grid-template-columns:1.3fr 1fr 1fr;gap:42px;padding-bottom:46px}
.ft img.flogo{height:46px;width:auto;margin-bottom:20px}
.ft h4{font-size:14px;letter-spacing:.1em;color:var(--fg);margin:0 0 18px;font-family:var(--disp);
  text-transform:uppercase}
.ft ul{list-style:none;margin:0;padding:0;display:grid;gap:6px}
.ft a{color:var(--fg-mute2);text-decoration:none;display:inline-block;
  padding:9px 0;min-height:40px;line-height:1.4}
.ft a:hover{color:var(--blue-dk)}
.ft-social{display:flex;gap:10px;margin-top:16px}
.ft-social a{width:40px;height:40px;border-radius:8px;background:var(--ink-2);border:1px solid var(--line);
  display:grid;place-items:center;padding:0;min-height:0}
.ft-social a:hover{border-color:var(--blue-dk);color:var(--blue-dk)}
.ft-bar{border-top:1px solid var(--line);padding:22px 0 30px;display:flex;
  flex-wrap:wrap;gap:12px 26px;align-items:center;justify-content:space-between;font-size:13px}
.ft-bar p{margin:0}
.by{color:var(--fg-mute2);text-decoration:none;padding:9px 0;
  display:inline-block;min-height:40px;font-family:var(--text)}
.by b{color:var(--blue-dk);font-weight:700}
.disc{border-top:1px solid var(--line);padding:22px 0 34px;font-size:12px;
  color:var(--fg-mute2);line-height:1.65}

/* the pinned bar flashes the hero form's rim after scrolling to it */
.hq-flash{animation:hqflash 1.4s ease}
@keyframes hqflash{
  0%,100%{box-shadow:0 24px 60px rgba(0,0,0,.42)}
  30%,70%{box-shadow:0 0 0 3px rgba(45,108,163,.85),0 24px 60px rgba(0,0,0,.42)}
}

/* ---------- motion ---------- */
.prog{position:fixed;left:0;top:0;height:3px;width:100%;transform:scaleX(0);
  transform-origin:left;background:var(--blue);z-index:80}
.reveal{opacity:0;transform:translateY(22px);filter:blur(6px);
  transition:opacity .7s cubic-bezier(.22,1,.36,1),transform .7s cubic-bezier(.22,1,.36,1),filter .7s}
.reveal.in{opacity:1;transform:none;filter:none}
.reveal[data-d="1"]{transition-delay:.08s}
.reveal[data-d="2"]{transition-delay:.16s}
.reveal[data-d="3"]{transition-delay:.24s}
.reveal[data-d="4"]{transition-delay:.32s}
.reveal[data-d="5"]{transition-delay:.4s}
.wc>span{display:inline-block;opacity:0;transform:translateY(24px);
  animation:wc .72s cubic-bezier(.22,1,.36,1) forwards}
@keyframes wc{to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){
  .reveal{opacity:1;transform:none;filter:none;transition:none}
  .wc>span{opacity:1;transform:none;animation:none}
  .btn:hover,.svc:hover{transform:none}
}

/* ---------- responsive ---------- */
@media(max-width:980px){
  .hero-in{grid-template-columns:1fr;gap:36px}
  .hero-fig{display:none}
  .story{grid-template-columns:1fr;gap:36px}
  .svcs,.quotes,.gal,.techs{grid-template-columns:repeat(2,minmax(0,1fr))}
  .stats{grid-template-columns:repeat(2,minmax(0,1fr))}
  .ct{grid-template-columns:1fr;gap:36px}
  .ft-in{grid-template-columns:1fr 1fr;gap:32px}
}
@media(max-width:640px){
  body{font-size:16px}
  .sec{padding:64px 0}
  .hero{padding:44px 0 70px}
  .hcta{display:none}          /* the hero form is the stronger call to action */
  .hh{font-size:clamp(32px,9.5vw,42px)}
  .hchips li{font-size:12px;padding:8px 13px}
  .svcs,.quotes,.gal,.techs{grid-template-columns:1fr}
  .gal-i{aspect-ratio:16/10}
  .fgrid{grid-template-columns:1fr}
  .accs-in{gap:16px 20px;justify-content:center}
  .accs p{width:100%;text-align:center;margin:0 0 4px}
  .accs-badge img{height:28px}.accs-badge img.rec{height:24px}
  .ft-in{grid-template-columns:1fr;gap:28px}
  .ft-bar{justify-content:flex-start}
  .shead{margin-bottom:38px}
  .council{gap:10px}
  main{padding-bottom:76px}    /* clear of the pinned bar */
}

/* =====================================================================
   MULTI-PAGE EXPANSION — 18 Aug 2026. Breadcrumb, inner-page hero, service
   detail, areas grid + suburb pages, work gallery, van-wrap, hidden
   feedback/star-gate, and a heavier motion layer. Chat widget CSS ships
   inline per page (its own <style> block, emitted after this file, so its
   pinbar-clearance rule always wins on source order). */

/* ---------- breadcrumb ---------- */
.crumb{display:flex;flex-wrap:wrap;align-items:center;gap:2px;padding:12px 0 0}
.crumb a{color:var(--fg-mute2);text-decoration:none;font-family:var(--disp);font-size:12.5px;
  letter-spacing:.03em;display:inline-flex;align-items:center;min-height:40px;padding:0 6px}
.crumb a:hover{color:var(--blue-dk)}
.crumb span{color:var(--fg-mute2);opacity:.5;display:inline-flex;align-items:center;min-height:40px}
.crumb b{color:var(--fg-mute);font-weight:500;font-family:var(--disp);font-size:12.5px;
  letter-spacing:.03em;display:inline-flex;align-items:center;min-height:40px;padding:0 6px}

/* ---------- inner page hero (service / area / about / contact / work) ---------- */
.phero{position:relative;background:var(--ink);color:var(--fg);overflow:hidden;
  padding:26px 0 64px;isolation:isolate}
.phero::after{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(200deg, rgba(245,242,238,.045), transparent 46%)}
.phero .hero-texture{opacity:.35}
.phero-in{max-width:760px}
.phero h1{font-size:clamp(32px,4.6vw,52px);margin:16px 0 18px;letter-spacing:-.01em}
.phero p{font-size:clamp(15.5px,1.3vw,17.5px);color:var(--fg-mute);max-width:38em;margin:0 0 26px}
.phero-price{display:inline-flex;align-items:baseline;gap:10px;background:rgba(45,108,163,.12);
  border:1px solid rgba(45,108,163,.34);border-radius:100px;padding:11px 22px;margin-bottom:8px}
.phero-price b{font-family:var(--disp);font-size:19px;color:var(--fg)}
.phero-price span{font-size:12.5px;color:var(--fg-mute2)}
.phero-cta{display:flex;flex-wrap:wrap;gap:14px;margin-top:20px}

/* ---------- service detail body ---------- */
.svc-body{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);gap:56px;align-items:start}
.svc-main h2{font-size:clamp(24px,2.6vw,30px);margin:0 0 16px}
.svc-main p{font-size:16px;color:var(--fg-mute)}
.svc-checks{list-style:none;margin:22px 0 34px;padding:0;display:grid;gap:12px}
.svc-checks li{display:flex;gap:12px;align-items:flex-start;font-size:15.5px;color:var(--fg-mute)}
.svc-checks svg{flex:0 0 auto;color:var(--blue-dk);margin-top:3px}
.svc-side{background:var(--ink-2);border:1px solid var(--line);border-radius:var(--r);
  padding:28px;position:sticky;top:96px}
.svc-side h3{font-size:18px;margin:0 0 14px}
.svc-side ul{list-style:none;margin:0 0 22px;padding:0;display:grid;gap:10px}
.svc-side li{display:flex;gap:10px;align-items:flex-start;font-size:14px;color:var(--fg-mute)}
.svc-side li::before{content:"";flex:0 0 auto;width:6px;height:6px;background:var(--blue);
  margin-top:7px;transform:rotate(45deg)}
.svc-related{display:grid;gap:8px;margin-top:6px}
.svc-related a{display:flex;justify-content:space-between;align-items:center;gap:8px;
  color:var(--fg);text-decoration:none;font-size:13.5px;font-weight:600;
  padding:12px 14px;border-radius:8px;background:var(--ink);border:1px solid var(--line)}
.svc-related a:hover{border-color:var(--blue-dk);color:var(--blue-dk)}

/* ---------- FAQ accordion ---------- */
.faq{max-width:820px;margin:0 auto;display:grid;gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.faq-i{background:var(--ink)}
.sec-tint .faq-i{background:var(--ink-2)}
.faq-q{width:100%;display:flex;align-items:center;justify-content:space-between;gap:16px;
  text-align:left;background:none;border:0;cursor:pointer;padding:22px 24px;
  font-family:var(--disp);font-size:16px;font-weight:600;color:var(--fg);min-height:56px}
.faq-q svg{flex:0 0 auto;color:var(--blue-dk);transition:transform .28s ease}
.faq-i.on .faq-q svg{transform:rotate(45deg)}
.faq-a{max-height:0;overflow:hidden;transition:max-height .32s ease}
.faq-a p{padding:0 24px 22px;margin:0;font-size:15px;color:var(--fg-mute);max-width:60em}
.faq-i.on .faq-a{max-height:280px}

/* ---------- areas index ---------- */
.subgrid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}
.subcard{display:block;text-decoration:none;background:var(--ink);border:1px solid var(--line);
  border-radius:var(--r);padding:20px 20px 18px;transition:transform .22s ease,border-color .22s ease,background .22s ease}
.sec-tint .subcard{background:var(--ink-2)}
.subcard:hover{transform:translateY(-4px);border-color:rgba(45,108,163,.5);background:rgba(45,108,163,.08)}
.subcard b{display:block;font-family:var(--disp);font-size:17px;color:var(--fg);margin-bottom:5px}
.subcard span{display:block;font-size:12.5px;color:var(--fg-mute2)}
/* Michael, 3 Sep: the map has to show the suburbs Glow works in and darken the
   ones it does not, which a Google satellite embed of the whole of Melbourne
   never did. It is now a drawn map of the real suburb boundaries, built by
   scripts/glow-area-map.mjs. Four files, because an external SVG loaded as a
   background never sees the page's custom properties, so the theme picks the
   file the same way --logo-h already does. The sm pair is cropped tighter and
   drops the twelve suburb labels, which are unreadable at 375px. */
.area-map{position:relative;border-radius:var(--r);overflow:hidden;border:1px solid var(--line);
  aspect-ratio:1600/912;margin:0 0 26px;box-shadow:0 30px 70px rgba(0,0,0,.35);
  background:var(--area-map) center/cover no-repeat}
.area-map::after{content:"";position:absolute;inset:0;pointer-events:none;border-radius:var(--r);
  box-shadow:inset 0 0 0 1px var(--line-2)}
@media (max-width:640px){.area-map{aspect-ratio:1282/630;background-image:var(--area-map-sm)}}
.council-tabs{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:36px}
.council-tabs a{font-family:var(--disp);font-size:13px;font-weight:600;letter-spacing:.03em;
  color:var(--fg-mute);text-decoration:none;border:1px solid var(--line-2);border-radius:100px;
  padding:10px 20px;transition:border-color .2s,color .2s}
.council-tabs a:hover{border-color:var(--blue-dk);color:var(--blue-dk)}

/* ---------- suburb page ---------- */
.sub-meta{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:22px}
.sub-meta span{font-family:var(--disp);font-size:12.5px;font-weight:600;letter-spacing:.05em;
  color:var(--blue-dk);background:rgba(45,108,163,.12);border:1px solid rgba(45,108,163,.32);
  border-radius:100px;padding:8px 16px}
.nearby{display:flex;flex-wrap:wrap;gap:10px;list-style:none;margin:0;padding:0}
.nearby a{font-family:var(--disp);font-size:13.5px;color:var(--fg-mute);text-decoration:none;
  border:1px solid var(--line-2);border-radius:100px;padding:10px 18px;display:inline-block;
  transition:border-color .2s,color .2s}
.nearby a:hover{border-color:var(--blue-dk);color:var(--blue-dk)}

/* ---------- work / gallery ---------- */
.wgal{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px}
.wcard{border-radius:var(--r);overflow:hidden;background:var(--ink-2);border:1px solid var(--line);
  position:relative}
.wcard .wimg{aspect-ratio:4/3;background:var(--ink-3);overflow:hidden;position:relative}
.wcard .wimg img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform .5s cubic-bezier(.22,1,.36,1)}
.wcard:hover .wimg img{transform:scale(1.06)}
.wcard .wtag{position:absolute;left:12px;top:12px;background:var(--scrim);
  backdrop-filter:blur(8px);border:1px solid var(--line-2);border-radius:100px;
  padding:6px 14px;font-family:var(--disp);font-size:11.5px;letter-spacing:.05em;
  color:var(--fg);font-weight:600}
.wcard .wcap{padding:16px 18px}
.wcard .wcap b{display:block;font-family:var(--disp);font-size:15px;color:var(--fg);margin-bottom:3px}
.wcard .wcap span{font-size:12.5px;color:var(--fg-mute2)}
.wnote{margin:30px auto 0;max-width:760px;text-align:center;font-size:13.5px;color:var(--fg-mute2)}
.wempty{max-width:640px;margin:0 auto;text-align:center;padding:56px 32px;
  border:1.5px dashed var(--line-2);border-radius:var(--r);background:var(--ink-2)}
.wempty svg{color:var(--blue-dk);margin-bottom:16px}
.wempty h3{font-size:20px;margin:0 0 10px}
.wempty p{font-size:14.5px;color:var(--fg-mute);margin:0}

/* ---------- brand-in-the-wild / van wrap (about page) ---------- */
.van{position:relative;border-radius:var(--r);overflow:hidden;border:1px solid var(--line);
  box-shadow:var(--sh)}
.van img{width:100%;height:auto;display:block}
.van-cap{display:flex;align-items:center;gap:10px;padding:16px 20px;background:var(--ink-2);
  border-top:1px solid var(--line);font-size:13px;color:var(--fg-mute2)}
.van-cap svg{flex:0 0 auto;color:var(--blue-dk)}

/* ---------- hidden star-gate / feedback page ---------- */
.gate{max-width:560px;margin:0 auto;text-align:center}
.stars{display:flex;justify-content:center;gap:10px;margin:30px 0 8px}
.star-btn{background:none;border:0;cursor:pointer;padding:8px;color:var(--fg-mute2);
  transition:color .2s,transform .2s;min-width:48px;min-height:48px}
.star-btn:hover,.star-btn:focus-visible{color:#ffb020;transform:scale(1.12)}
.star-btn.picked{color:#ffb020}
.gate-note{font-size:13.5px;color:var(--fg-mute2);margin-top:6px}
.gate-panel{display:none;margin-top:30px;text-align:left}
.gate-panel.on{display:block}
.gate-good{text-align:center;padding:40px 30px;background:var(--ink-2);border:1px solid var(--line);
  border-radius:var(--r)}
.gate-good svg{color:#3ccB78;margin-bottom:14px}
.gate-good h3{font-size:22px;margin:0 0 10px}
.gate-good p{color:var(--fg-mute);margin:0 0 22px}

/* ---------- heavier motion layer ---------- */
.reveal-l{opacity:0;transform:translateX(-28px);filter:blur(6px);
  transition:opacity .75s cubic-bezier(.22,1,.36,1),transform .75s cubic-bezier(.22,1,.36,1),filter .75s}
.reveal-l.in{opacity:1;transform:none;filter:none}
.reveal-r{opacity:0;transform:translateX(28px);filter:blur(6px);
  transition:opacity .75s cubic-bezier(.22,1,.36,1),transform .75s cubic-bezier(.22,1,.36,1),filter .75s}
.reveal-r.in{opacity:1;transform:none;filter:none}
.reveal-sc{opacity:0;transform:scale(.94);transition:opacity .6s ease,transform .6s cubic-bezier(.22,1,.36,1)}
.reveal-sc.in{opacity:1;transform:none}

/* The 28px sideways travel on .reveal-l and .reveal-r parks the element 28px
   right of its track before the reveal fires, while .wrap only leaves a 20px
   right gutter, so anything filling its track sticks 8px past the screen and
   the whole page scrolls sideways.
   THE BREAKPOINT IS NOT A PHONE BREAKPOINT. It was written as 760px when this
   was first read as a phone problem, which left the same 8px scroll live on
   every width from 761 up to 1375, the iPhone Duo unfolded at 890 and the
   1280 and 1366 laptops among them. .wrap is min(100% - 40px, 1320px), so the
   right gutter only reaches 28px once the viewport hits 1376. Below that there
   is no room to move across, so the motion travels on Y instead. Above it the
   sideways travel fits and is kept. Measured 11 Sep 2026, not guessed. */
@media(max-width:1375px){
  .reveal-l,.reveal-r{transform:translateY(18px)}
  .reveal-l.in,.reveal-r.in{transform:none}
}

/* rotating hero photographs. Four real shots of Michael and the van, crossfading.
   Each slide sits in the same box and only opacity moves, so nothing lays out or
   repaints on the swap. The .is-rot class raises specificity above the single
   image rule, which otherwise pins hero photo opacity at .13. */
.secbg-img.is-rot picture{position:absolute;inset:0;display:block}
.hero .secbg-img.is-rot img{opacity:0;transition:opacity 1.6s ease;
  animation:kb 26s ease-in-out infinite alternate}
.hero .secbg-img.is-rot img.on{opacity:.3}

/* hero trust row, replacing the two stacked eyebrow lines */
.htrust{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 18px;padding:0;list-style:none}
.htrust li{font-family:var(--disp);font-size:12px;letter-spacing:.09em;text-transform:uppercase;
  font-weight:600;color:var(--fg-mute);background:rgba(255,255,255,.05);
  border:1px solid rgba(255,255,255,.12);border-radius:999px;padding:7px 13px;white-space:nowrap}

/* the middle pill is the live one: a slow blue gradient sweep using the van's
   brighter #0082DD, which is decorative only and never carries text. The label
   sits in its own layer above the sweep so it stays solid white and readable. */
.htrust li.is-live{position:relative;color:#fff;border-color:rgba(0,130,221,.42);
  background:linear-gradient(100deg,
    rgba(45,108,163,.22) 0%, rgba(0,130,221,.55) 35%,
    rgba(98,145,186,.30) 62%, rgba(45,108,163,.22) 100%);
  background-size:280% 100%;
  animation:htsweep 8s ease-in-out infinite;
  box-shadow:0 0 0 1px rgba(0,130,221,.16), 0 6px 22px -12px rgba(0,130,221,.85)}
@keyframes htsweep{
  0%{background-position:0% 50%}
  50%{background-position:100% 50%}
  100%{background-position:0% 50%}
}

/* Google review badge */
.grev{display:inline-flex;align-items:center;gap:9px;margin:0 0 22px;text-decoration:none;
  background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.13);
  border-radius:12px;padding:9px 14px;color:var(--fg);
  transition:border-color .25s ease,background .25s ease}
.grev:hover,.grev:focus-visible{border-color:rgba(255,255,255,.26);background:rgba(255,255,255,.08)}
.grev-g{width:17px;height:17px;flex:none}
.grev b{font-family:var(--disp);font-size:15px;line-height:1}
.grev .gstars{display:inline-flex;gap:2px;color:#F5B301}
.grev .gstars svg{width:13px;height:13px}
.grev-n{font-size:13px;color:var(--fg-mute2)}

/* a motion flag the scripts read, so no script has to name the media feature */
:root{--motion:1}
@media(prefers-reduced-motion:reduce){
  :root{--motion:0}
  .hero .secbg-img.is-rot img{animation:none;transition:none}
  .htrust li.is-live{animation:none;background-position:40% 50%}
}
@media(max-width:640px){
  .htrust li{font-size:11px;letter-spacing:.06em;padding:6px 11px}
}
@media(max-width:420px){
  .grev{gap:7px;padding:8px 12px}
  .grev b{font-size:14px}
  .grev .gstars svg{width:12px;height:12px}
  .grev-n{font-size:12px}
}


/* animated plug icon — a slow pulse + spark on the header brand mark on hover;
   entirely decorative, never blocks a tap target */
@keyframes pulse-dot{0%,100%{opacity:1}50%{opacity:.55}}
.brand{transition:transform .3s cubic-bezier(.22,1,.36,1)}
.brand:hover{transform:translateY(-1px)}

/* parallax depth — desktop only, driven by g4.js setting --py per layer;
   inert (transform:none) until JS opts a layer in, so it never shifts
   content before scroll math runs */
.px{will-change:transform}
.hero-texture,.hero-chevron{transform:translateY(var(--py,0px))}

/* section reveal on the hex/chevron motif, echoing the van wrap panel cut,
   used as a divider between major sections on inner pages */
.hexdiv{height:64px;position:relative;overflow:hidden;pointer-events:none}
.hexdiv::before{content:"";position:absolute;inset:0;opacity:.4;
  background-image:linear-gradient(var(--mesh) 1px,transparent 1px),
    linear-gradient(90deg,var(--mesh) 1px,transparent 1px);
  background-size:22px 22px;
  mask-image:linear-gradient(90deg, transparent, #000 30%, #000 70%, transparent);
  -webkit-mask-image:linear-gradient(90deg, transparent, #000 30%, #000 70%, transparent);
  transform:scaleX(0);transform-origin:center;transition:transform 1s cubic-bezier(.22,1,.36,1)}
.hexdiv.in::before{transform:none}

/* magnetic-ish button hover, subtle, transform/opacity only */
.btn{position:relative}
/* 22EW's primary button carries an expanding cyan glow ring. Glow's carries a
   precise hairline that draws itself along the bottom edge instead — the same
   "something happened" feedback, expressed as a rule rather than a glow. */
.btn-pri::after{content:"";position:absolute;left:0;right:100%;bottom:-1px;height:2px;
  background:var(--fg);opacity:.85;transition:right .35s var(--ease)}
.btn-pri:hover::after{right:0}

/* stat / counter emphasis on scroll-in */
.stat b{transition:color .3s ease}
.stat.in b{color:var(--blue-dk)}

@media(prefers-reduced-motion:reduce){
  .reveal-l,.reveal-r,.reveal-sc{opacity:1;transform:none;filter:none;transition:none}
  .hexdiv::before{transform:none;transition:none}
  .hero-texture,.hero-chevron{transform:none}
  .wcard:hover .wimg img{transform:none}
}

/* ---------- short liquid-glass enquiry form (inner pages) ---------- */
.gform{max-width:640px;margin:0 auto;position:relative;border-radius:18px;overflow:hidden;
  background:linear-gradient(165deg,var(--glass-hi),transparent 40%,var(--glass-hi)),
    var(--glass-bg);
  backdrop-filter:blur(30px) saturate(165%);-webkit-backdrop-filter:blur(30px) saturate(165%);
  border:1px solid var(--glass-line);box-shadow:var(--sh)}
.gform::before{content:"";position:absolute;top:0;left:0;width:70%;height:60%;pointer-events:none;
  background:radial-gradient(ellipse at top left, var(--glass-hi-2), transparent 68%)}
.gform::after{content:"";position:absolute;left:14px;right:14px;bottom:0;height:2px;
  background:linear-gradient(90deg,transparent,rgba(45,108,163,.85),transparent)}
@supports not (backdrop-filter:blur(1px)){
  .gform{background:var(--ink-2)}
}
.gform-brand{position:relative;display:grid;place-items:center;padding:26px 24px 20px;
  background:linear-gradient(180deg,rgba(45,108,163,.24),transparent)}
.gform-brand img{height:64px;width:auto}
.gform-in{position:relative;padding:8px 30px 32px}
.gform-in h3{font-size:22px;margin:0 0 8px;text-align:center}
.gform-in>p{font-size:14.5px;color:var(--fg-mute);text-align:center;margin:0 0 24px}
@media(max-width:640px){
  .gform-brand img{height:50px}
  .gform-in{padding:6px 20px 26px}
}

/* ---------- responsive: multi-page additions ---------- */
@media(max-width:980px){
  .svc-body{grid-template-columns:1fr;gap:32px}
  .svc-side{position:static}
  .subgrid{grid-template-columns:repeat(3,minmax(0,1fr))}
  .wgal{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media(max-width:640px){
  .phero{padding:18px 0 44px}
  .subgrid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .wgal{grid-template-columns:1fr}
  .faq-q{font-size:15px;padding:18px 18px}
  .faq-a p{padding:0 18px 18px}
  .council-tabs{gap:8px}
  .council-tabs a{padding:9px 16px;font-size:12.5px}
  .stars{gap:4px}
  .star-btn{padding:6px;min-width:42px}
}

/* ===========================================================================
   ADDITIONS — 18 Aug 2026 revision round.
   Editorial / spec-sheet layer, faded Ken Burns section backgrounds, the
   aligned service card grid, the moving review rail, and the hero work ticker.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   FADED SECTION BACKGROUNDS with a slow Ken Burns drift.
   AI-generated illustrative imagery (see the bg/manifest.json _note) used ONLY
   as section wallpaper — never captioned, never presented as a photo of a real
   Glow job, and always behind a veil.

   CONTRAST, measured rather than eyeballed. The audit lesson is that a
   translucent layer has to be composited down onto what is behind it before
   computing a ratio. Worst case here is a PURE WHITE pixel of the photograph
   showing through at full --bgfade, so the effective ground becomes
   mix(#fff, ground, .07). Against that ground:
       --fg        13.9 : 1
       --fg-mute    7.9 : 1
       --fg-mute2   5.1 : 1
       --blue-dk    5.0 : 1
   all clear of 4.5:1. Two rules follow from the same maths and must be kept:
     1. --bgfade never goes above .07;
     2. .secbg is only ever put on an --ink or --ink-2 ground. On the lighter
        --ink-3 ground --blue-dk drops to 4.21:1 and fails, so .sec-dark
        sections do NOT get a background image.
   --------------------------------------------------------------------------- */
:root{ --bgfade:.07 }
.secbg{position:relative;isolation:isolate;overflow:hidden}
.secbg>.wrap{position:relative;z-index:2}
.secbg-img{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.secbg-img img{width:100%;height:100%;object-fit:cover;object-position:center;
  opacity:var(--bgfade);filter:grayscale(.4) brightness(.9);
  transform:scale(1.06);transform-origin:center;
  animation:kb 34s ease-in-out infinite alternate;will-change:transform}
/* transform-only, so it never triggers layout or paint on scroll */
@keyframes kb{from{transform:scale(1.06)}to{transform:scale(1.19)}}
/* the veil sits ABOVE the photo and below the content, and only ever darkens */
.secbg-img::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,var(--ink) 0%,rgba(var(--ink-rgb),.35) 22%,rgba(var(--ink-rgb),.35) 78%,var(--ink) 100%)}
.sec-tint .secbg-img::after{
  background:linear-gradient(180deg,var(--ink-2) 0%,rgba(var(--ink-rgb),.28) 22%,rgba(var(--ink-rgb),.28) 78%,var(--ink-2) 100%)}
@media(prefers-reduced-motion:reduce){
  .secbg-img img{animation:none;transform:scale(1.06)}
}
/* phones get the 800px derivative and no animation at all — a 34s transform on
   a full-bleed image is the one thing on this page that could cost battery */
@media(max-width:640px){
  .secbg-img img{animation:none;transform:none}
}

/* ---------------------------------------------------------------------------
   EDITORIAL SECTION HEAD.
   22EW opens every section with a centred cyan pill eyebrow above a heavy
   uppercase heading. Glow opens with a numbered spec-sheet row: an index, a
   hairline rule that runs the full width of the section, and a large heading
   set left in a narrow measure with a lot of air to its right. Asymmetric on
   purpose — the point of the brief was Tesla and West Cork, not a marketing
   template.
   --------------------------------------------------------------------------- */
.shead{max-width:640px;margin:0 0 52px}
.shead.mid{margin-inline:auto;text-align:center}
.shead h2{font-size:clamp(30px,4.6vw,52px);margin:0 0 18px;letter-spacing:-.022em}

.ehead{margin:0 0 56px}
.ehead-top{display:flex;align-items:baseline;gap:18px;
  padding-bottom:14px;border-bottom:1px solid var(--line-2);margin-bottom:34px}
.ehead-ix{font-family:var(--disp);font-size:12.5px;font-weight:600;letter-spacing:.16em;
  color:var(--blue-dk);flex:0 0 auto;font-variant-numeric:tabular-nums}
.ehead-lbl{font-family:var(--disp);font-size:12.5px;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--fg-mute2);margin:0}
.ehead-meta{margin-left:auto;font-family:var(--disp);font-size:12px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--fg-mute2);flex:0 0 auto}
.ehead-in{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);
  gap:20px 60px;align-items:end}
.ehead h2{font-size:clamp(31px,4.9vw,58px);margin:0;letter-spacing:-.024em;line-height:1.02;
  max-width:15ch}
.ehead p{font-size:16.5px;color:var(--fg-mute);margin:0;max-width:46ch}
@media(max-width:900px){
  .ehead{margin-bottom:40px}
  .ehead-in{grid-template-columns:1fr;gap:18px;align-items:start}
  .ehead-meta{display:none}
}

/* ---------------------------------------------------------------------------
   SERVICE CARDS — Tim, 18 Aug: "make this more attractive" + line them up.
   Four separate faults were reported and each is fixed structurally:
     1. DUPLICATE LABEL — the card printed the service name as a grey eyebrow
        AND again as the heading. The eyebrow is now the INDEX + category
        ("01 / Electrical"), so it carries information instead of repeating.
     2. NOTHING ALIGNED — price rows and Learn more buttons sat at different
        heights because each card was an independent flow. The card is now a
        6-row grid with named, FIXED rows, so the icon, eyebrow, heading,
        description, price row and button each sit on one shared baseline
        across all three cards no matter how long the copy is.
     3. INCONSISTENT PRICE CAPTIONS — one card said "Depending on the scope,
        indicative" and the others "Indicative range". Normalised in data.js to
        "Indicative range" on every service.
     4. THE UNEXPLAINED BLUE TOP BORDER on card 3 was a hover artefact
        (.svc::before scaleX(0) -> 1). It is now a deliberate, labelled
        "Most requested" treatment on exactly one card, and the hover artefact
        is gone.
   --------------------------------------------------------------------------- */
.svcs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0;
  border-top:1px solid var(--line-2)}
.svc{background:transparent;border:0;border-bottom:1px solid var(--line-2);
  border-right:1px solid var(--line);border-radius:0;
  padding:34px 30px 30px;position:relative;overflow:visible;
  /* the shared 6-row grid — this is what makes every row line up */
  display:grid;grid-template-rows:auto auto auto 1fr auto auto;gap:0;
  transition:background .25s ease}
.svcs .svc:last-child{border-right:0}
.sec-tint .svc{background:transparent}
.svc:hover{background:var(--tint-2)}
.svc::before{content:none}
.svc-ico{width:42px;height:42px;border:1px solid var(--blue-line);border-radius:2px;
  display:grid;place-items:center;color:var(--blue-dk);margin:0 0 22px;
  transition:border-color .25s ease,color .25s ease}
.svc:hover .svc-ico{border-color:var(--blue-dk)}
.svc-n{font-family:var(--disp);font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--fg-mute2);font-weight:600;display:flex;align-items:center;gap:10px;
  margin:0 0 12px;min-height:17px}
.svc-n i{font-style:normal;color:var(--blue-dk);font-variant-numeric:tabular-nums}
.svc-n s{text-decoration:none;color:var(--line-2)}
.svc h3{font-size:22px;margin:0 0 14px;letter-spacing:-.016em;min-height:2.12em;
  display:flex;align-items:flex-start}
.svc h3 a{color:inherit;text-decoration:none}
.svc>p{font-size:15px;margin:0 0 22px;color:var(--fg-mute);line-height:1.6}
.svc-price{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;
  padding:16px 0 18px;border-top:1px solid var(--line);margin:0 0 20px;min-height:74px}
.svc-price b{font-family:var(--disp);font-size:21px;color:var(--fg);letter-spacing:-.02em;
  font-variant-numeric:tabular-nums}
.svc-price span{font-size:11.5px;color:var(--fg-mute2);letter-spacing:.09em;
  text-transform:uppercase;font-family:var(--disp)}
.svc .btn{justify-self:start}
/* the ONE deliberate featured card, replacing the accidental hover border */
.svc.is-featured{background:rgba(45,108,163,.055);border-top:2px solid var(--blue);
  margin-top:-1px}
.svc.is-featured:hover{background:rgba(45,108,163,.085)}
.svc-flag{position:absolute;top:-1px;right:0;transform:translateY(-100%);
  font-family:var(--disp);font-size:10.5px;font-weight:600;letter-spacing:.16em;
  text-transform:uppercase;color:var(--blue-dk);padding:0 0 8px}
@media(max-width:980px){
  .svcs{grid-template-columns:repeat(2,minmax(0,1fr))}
  .svcs .svc:nth-child(2n){border-right:0}
  .svcs .svc:last-child{border-right:0}
}
@media(max-width:640px){
  .svcs{grid-template-columns:1fr;border-top:0}
  .svc{border-right:0;padding:28px 0 26px}
  /* on one column the shared row heights are pointless and only add holes,
     so the min-heights that force cross-card alignment are released */
  .svc h3{min-height:0}
  .svc-price{min-height:0}
  .svc.is-featured{padding-left:18px;padding-right:18px;margin-inline:-18px;
    border-top-width:2px}
  .svc-flag{position:static;transform:none;padding:0 0 10px;display:block}
}

/* ---------------------------------------------------------------------------
   MOVING REVIEW RAIL.
   Two placements share one component:
     .rstrip  — the Eden BL treatment, review cards floating at the bottom of
                the hero, in normal flow so nothing overlaps the headline.
     .rmarq   — the "In their words" section, which Tim asked to have "moving
                along so you see more".
   Glow has exactly THREE published reviews and no more were invented. The
   track duplicates that real set to make the loop seamless (the duplicates are
   aria-hidden so a screen reader hears each review once), and the note under
   the section says plainly that there are three.
   Deliberately NOT the 22EW review strip: no glass, no backdrop-filter, no
   blue glow — flat hairline-ruled spec cards, and a warm edge mask.
   --------------------------------------------------------------------------- */
.rrail{position:relative;overflow:hidden;width:100%;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent)}
.rrail-track{display:flex;gap:20px;width:max-content;padding:2px 20px;
  animation:rroll 56s linear infinite;will-change:transform}
@keyframes rroll{to{transform:translateX(-50%)}}
.rrail:hover .rrail-track,.rrail:focus-within .rrail-track{animation-play-state:paused}
@media(prefers-reduced-motion:reduce){.rrail-track{animation:none}}
.rcard{flex:0 0 clamp(280px,31vw,392px);max-width:392px;
  background:var(--ink-2);border:1px solid var(--line);border-left:2px solid var(--blue-line);
  border-radius:var(--r);padding:24px 26px 22px;
  display:flex;flex-direction:column;gap:12px}
.rcard-top{display:flex;align-items:center;justify-content:space-between;gap:12px}
.rcard-src{font-family:var(--disp);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--fg-mute2)}
.rcard p{font-size:15px;line-height:1.6;color:var(--fg);margin:0;font-weight:400}
.rcard b{margin-top:auto;font-family:var(--disp);font-size:13.5px;letter-spacing:.04em;
  color:var(--blue-dk);font-weight:600}
/* the review rail floating at the bottom of the hero, Eden BL style. In NORMAL
   FLOW as the hero's last child — the Eden source carries a comment recording
   that an earlier absolute version overlapped the copy and was moved into
   flow, so that resolved value is what gets lifted, not the original idea. */
.hero.has-rstrip{display:flex;flex-direction:column;padding-bottom:40px}
.hero.has-rstrip .hero-in{flex:1;align-content:center}
.rstrip{margin-top:52px;position:relative;z-index:2}
.rstrip .rcard{background:rgba(var(--ink-rgb),.72);
  backdrop-filter:blur(14px) saturate(130%);-webkit-backdrop-filter:blur(14px) saturate(130%);
  border-color:var(--line-2)}
@supports not (backdrop-filter:blur(1px)){ .rstrip .rcard{background:var(--ink-2)} }
.rstrip .rcard p{font-size:14.5px;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
/* Eden hides its strip entirely under 980px. Glow keeps it — the reviews are
   the strongest thing on the page for a trades client and a phone is where
   most of the traffic lands — but drops to one narrower card in view, still a
   full 44px+ tap-free read, and stops the marquee competing with a thumb by
   keeping it a slow single-axis transform that never captures touch. */
.rrail-track{touch-action:pan-y}
@media(max-width:640px){
  .rstrip{margin-top:34px}
  .rcard{flex-basis:clamp(250px,80vw,300px);padding:20px 20px 18px}
  .rcard p{font-size:14px}
  .rrail-track{gap:14px;padding:2px 14px;animation-duration:44s}
}

/* ---------------------------------------------------------------------------
   HERO RECENT WORK TICKER — Tim: "just the text", at the top of the hero.
   Real completed ServiceM8 jobs, filtered to the real service area, and
   PUBLIC-SAFE BY SHAPE: the feed it reads carries only {type, suburb, date}.
   There is no name, no address, no job note and no photo id in it, so there is
   nothing here that could leak the way the gallery screenshot did.
   --------------------------------------------------------------------------- */
.tick{position:relative;z-index:2;border-bottom:1px solid var(--line);
  background:rgba(var(--ink-rgb),.5);overflow:hidden}
.tick-in{display:flex;align-items:center;gap:0;min-height:44px}
.tick-lbl{flex:0 0 auto;display:flex;align-items:center;gap:9px;padding:0 18px 0 0;
  margin-right:18px;border-right:1px solid var(--line);
  font-family:var(--disp);font-size:10.5px;font-weight:600;letter-spacing:.16em;
  text-transform:uppercase;color:var(--fg-mute2);white-space:nowrap}
.tick-dot{width:6px;height:6px;border-radius:50%;background:var(--blue-dk);flex:0 0 auto;
  animation:pulse-dot 2.4s ease-in-out infinite}
.tick-vp{flex:1 1 auto;overflow:hidden;position:relative;
  /* masked at BOTH edges. With only a right hand mask the loop point showed a
     hard vertical cut on the left where an entry was sliced mid word. */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 6%,#000 92%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 6%,#000 92%,transparent)}
.tick-track{display:flex;width:max-content;animation:troll 96s linear infinite;
  will-change:transform}
@keyframes troll{to{transform:translateX(-50%)}}
.tick:hover .tick-track{animation-play-state:paused}
.tick-track span{display:inline-flex;align-items:center;gap:10px;padding-right:34px;
  font-size:13px;color:var(--fg-mute);white-space:nowrap}
.tick-track span b{font-weight:600;color:var(--fg)}
.tick-track span::after{content:"";width:3px;height:3px;border-radius:50%;
  background:var(--line-2);margin-left:24px;order:9}
.tick-track em{font-style:normal;color:var(--fg-mute2);font-size:12px;
  font-variant-numeric:tabular-nums}
@media(prefers-reduced-motion:reduce){
  .tick-track{animation:none}
  .tick-dot{animation:none}
}
@media(max-width:640px){
  .tick-lbl{font-size:10px;padding-right:12px;margin-right:12px;letter-spacing:.12em}
  .tick-lbl .tick-word{display:none}
  .tick-track span{font-size:12.5px;padding-right:26px}
}

/* ---------------------------------------------------------------------------
   HERO STAT BAND — real ServiceM8 numbers, with their qualifiers attached.
   The two figures describe DIFFERENT things and must never be run together:
   Michael's 25+ years is his personal experience in the trade, while the 579
   completed jobs sit inside a ServiceM8 record that only starts in Nov 2021.
   Each number therefore carries its own qualifying line in the markup.
   Final values are baked into the HTML and JS only winds them BACK, so a
   screenshot or a PDF never shows a row of zeros.
   --------------------------------------------------------------------------- */
.hstats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:0;
  margin:52px 0 0;border-top:1px solid var(--line-2)}
/* Even air on BOTH sides of every divider. The old rule only padded the right,
   so the number and its label started exactly ON the rule to their left, at
   zero clearance, while the right side carried 26px. The first column keeps
   padding-left:0 so it stays locked to the page grid under the headline. */
.hstat{padding:22px 26px;border-right:1px solid var(--line)}
.hstat:first-child{padding-left:0}
.hstat:last-child{border-right:0}
.hstat b{display:block;font-family:var(--disp);font-size:clamp(24px,2.5vw,32px);
  color:var(--fg);letter-spacing:-.03em;line-height:1;margin-bottom:7px;
  font-variant-numeric:tabular-nums}
.hstat span{display:block;font-size:12.5px;color:var(--fg-mute2);line-height:1.42}
@media(max-width:760px){
  .hstats{grid-template-columns:repeat(2,minmax(0,1fr))}
  .hstat{padding:18px 18px;border-bottom:1px solid var(--line)}
  /* two up, so the FIRST OF EACH ROW is the one that holds the grid edge */
  .hstat:nth-child(2n+1){padding-left:0}
  /* two columns means a label can take two lines, so the band grows to match
     and the descriptions stay on one line with each other */
  .hstat span b{min-height:2.7em}
  .hstat:nth-child(2n){border-right:0}
  .hstat:nth-last-child(-n+2){border-bottom:0}
}

/* ---------------------------------------------------------------------------
   CONTACT — Tim: "dont push too far to the outer edges".
   The contact block is the only section on the page whose inner grid was
   allowed to run the full 1320px measure, so on a wide screen the phone
   number sat hard left and the form hard right with nothing holding them
   together. It now takes a narrower measure of its own inside the wrap.
   --------------------------------------------------------------------------- */
.ct{display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1fr);gap:72px;
  align-items:start;max-width:1060px;margin-inline:auto}
/* This block sits at the END of the stylesheet, so it beats the earlier
   @media(max-width:980px){.ct{grid-template-columns:1fr}} on source order and
   the contact block would have stayed two columns on a phone. The stacking
   breakpoint has to be restated here, after the base rule, or mobile breaks. */
@media(max-width:980px){ .ct{grid-template-columns:1fr;gap:40px;max-width:640px} }
@media(max-width:640px){ .ct{gap:32px} }

/* --- plain (non link) values in the contact list --------------------------
   These were inline styles repeated on four elements; same look, one place. */
.ct-plain{color:var(--fg);font-size:16.5px;font-weight:600;display:inline-block;padding:9px 0}

/* --- .svcs used as a vertical LIST (suburb pages) -------------------------
   The suburb pages reuse .svc as a stacked link list. The alignment grid that
   makes three cards line up across a row is meaningless in one column and its
   min-heights only open holes, so those pages opt out explicitly rather than
   fighting it with inline styles. */
.svcs.is-list{grid-template-columns:1fr;gap:14px;border-top:0}
.svcs.is-list .svc{grid-template-rows:none;display:block;border:1px solid var(--line);
  border-radius:var(--r);padding:22px 24px;text-decoration:none;color:inherit}
.svcs.is-list .svc h3{min-height:0;margin-bottom:10px;font-size:19px;display:block}
.svcs.is-list .svc>p{margin-bottom:14px}
.svcs.is-list .svc-price{min-height:0;margin-bottom:0;padding-bottom:0}
.svcs.is-list .svc-ico{margin-bottom:14px}

/* --- WORK CARDS — Tim: "make centered" -----------------------------------
   The cards were a mix of portrait phone photos and one landscape screenshot
   dropped into a 4:3 frame, so each one cropped to a different part of its
   subject and the row read as ragged. Every card now uses ONE ratio with an
   explicitly centred focal point, so the grid is even whatever shape the
   source photo is. 3:2 rather than 4:3 because every real ServiceM8 photo on
   this account is a 3:4 portrait phone shot, and a shallower frame keeps more
   of the subject and less of the ceiling. */
.wcard .wimg{aspect-ratio:3/2;background:var(--ink-3);overflow:hidden;position:relative}
.wcard .wimg img{width:100%;height:100%;object-fit:cover;object-position:center center;
  display:block}
/* a two-card row would otherwise stretch across three columns and look broken.
   The gallery tracks the real number of photos rather than assuming three. */
.wgal{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px;
  justify-content:center}
.wgal[data-n="1"]{grid-template-columns:minmax(0,420px)}
.wgal[data-n="2"]{grid-template-columns:repeat(2,minmax(0,420px))}
@media(max-width:980px){
  .wgal,.wgal[data-n="2"]{grid-template-columns:repeat(2,minmax(0,1fr))}
  .wgal[data-n="1"]{grid-template-columns:1fr}
}
@media(max-width:640px){
  .wgal,.wgal[data-n="1"],.wgal[data-n="2"]{grid-template-columns:1fr}
}

/* --- review card fixes ---------------------------------------------------
   <blockquote> carries a 40px UA margin, which indented every review inside
   the rail and made the cards look broken. */
.rcard blockquote{margin:0;padding:0;border:0}
.rcard figcaption{margin:0}

/* --- hero stat band, tightened ------------------------------------------
   The qualifier line under each figure was running to three cramped lines.
   The label is now the readable line and the qualifier sits under it a step
   quieter, so the band reads as a spec row rather than a paragraph. */
.hstat span{font-size:12px;line-height:1.4;color:var(--fg-mute2)}
.hstat span b{display:block;font-weight:600;color:var(--fg-mute);font-size:12.5px;
  min-height:1.35em;margin-bottom:3px;
  font-family:var(--text);margin-bottom:2px}

/* --- hero background, lifted --------------------------------------------
   The veil is the only thing between the photo and the type, and it only ever
   DARKENS, so easing it back cannot reduce contrast below the figure already
   computed for a bare --bgfade over the ground. Tim asked for "super faded",
   and at the original veil strength the van was not perceptible at all. */
.hero .secbg-img::after{
  background:linear-gradient(180deg,rgba(var(--ink-rgb),.72) 0%,rgba(var(--ink-rgb),.30) 34%,rgba(var(--ink-rgb),.62) 82%,var(--ink) 100%)}
.hero .secbg-img img{opacity:.13;filter:grayscale(.25) brightness(.95)}

/* --- TAP TARGETS, from the programmatic mobile audit ---------------------
   Three classes of link were rendering under the 40px minimum. Screenshots
   never show this, which is exactly why the audit prints it.
     1. the service card heading link was a bare 23px inline <a>;
     2. the "See all six services / recent work / every suburb" links were
        16 to 38px of inline text;
     3. the same pattern on the .qnote / .wnote footnote links.
   Each is given real padding and pulled back with a negative margin so the
   visual layout is unchanged and only the hit area grows. */
.svc h3{align-items:stretch}
.svc h3 a{display:flex;align-items:flex-start;width:100%;
  padding:9px 0;margin:-9px 0;min-height:40px}
.price-note a,.wnote a,.qnote a{display:inline-block;padding:11px 4px;margin:-11px -4px;
  min-height:44px;line-height:1.5}

/* --- defensive: hero content always sits above its own background layers ---
   The veil inside .secbg-img is absolutely positioned at z-index 0, so a hero
   that carries a background image but is missing the .secbg class paints the
   veil straight over the headline. That shipped once and measured 4.28:1 on
   an h1 that should be 13:1, and no audit caught it — only sampling the real
   rendered pixels did. Pinning the hero's own wraps here means the class can
   never be forgotten into a contrast failure again. */
.hero>.wrap,.phero>.wrap{position:relative;z-index:2}

/* ===========================================================================
   THE GLOW LANGUAGE — 18 Aug 2026.
   Tim: "we need the whole site to be very glowy."

   The business is called GLOW and the mark is a plug inside a ring, so light is
   the brand's own idea rather than a decoration bolted on. This section is one
   coherent light language applied in the same way everywhere — the logo, the
   active nav item, the primary button, card edges, focus rings, section rules,
   the plug-ring motif — rather than a scattering of unrelated shadows.

   THE CONSTRAINT THAT SHAPES ALL OF IT. 22 Electrical Works is also a Scalus
   client, also an electrician in Melbourne's east, and their entire look is a
   cold blue-black ground with a GLOWING CYAN #00A2E2 used as a soft halo on
   almost every element. Glow's glow therefore had to be a different KIND of
   light, not the same effect in a different hue:
     - the colour is the brand blue lifted (#4E97D4), never a cyan. Its hue sits
       at 209 against 22EW's 197, and more importantly it is desaturated toward
       the warm ground rather than pushed toward neon;
     - the light is TIGHT. 22EW's halos run 20 to 40px of soft spread; Glow's
       resting glow is a 1px edge and a 6 to 8px core, which reads as an
       engineered signal rather than a neon wash;
     - it is INTERMITTENT. Almost nothing glows continuously. The logo pulses on
       a long cycle, buttons and cards light on interaction, and the rest is
       dark until something happens. "Here and there", per Tim.
   The result is closer to an instrument panel than a sign, which is what the
   Tesla and West Cork references in the brief were pointing at.

   PERFORMANCE AND ACCESSIBILITY, non negotiable:
     - every animated property is opacity, transform or filter, so nothing here
       triggers layout or paint on scroll;
     - prefers-reduced-motion gets a genuinely STATIC fallback, not a slower
       animation — the glow settles at its resting value and stops;
     - phones drop the continuous animations entirely (see the mobile block at
       the end), so the glow costs nothing on the device most of this traffic
       arrives on;
     - no glow is ever placed behind or under text in a way that lifts the
       ground the text sits on. Glows attach to borders, icons and edges. The
       one exception, the logo, carries no live text.
   =========================================================================== */
:root,
:root[data-theme="dark"]{
  --glow-rgb:78,151,212;               /* #4E97D4 — the brand blue, lifted */
  --glow-edge:0 0 0 1px rgba(78,151,212,.42);
  --glow-tight:0 0 8px rgba(78,151,212,.5);
  --glow-soft:0 0 22px rgba(78,151,212,.22);
  --glow-rest:.34;                     /* resting opacity of an idle glow */
  --glow-peak:.92;                     /* the brief moment it lifts */
}
/* On a light ground the same shadow reads as grey smudge rather than light, so
   the light theme uses the UNLIFTED blue at lower alpha and a tighter radius.
   A glow on paper has to be a saturated edge, not a halo. */
:root[data-theme="light"]{
  --glow-rgb:45,108,163;
  --glow-edge:0 0 0 1px rgba(45,108,163,.38);
  --glow-tight:0 0 6px rgba(45,108,163,.34);
  --glow-soft:0 0 16px rgba(45,108,163,.14);
  --glow-rest:.3;
  --glow-peak:.8;
}

/* ---------- focus, everywhere ---------------------------------------------
   One visible focus ring for the whole site, drawn as glow rather than as the
   UA outline. Keyboard users get the same light language everyone else gets. */
a:focus-visible,button:focus-visible,input:focus-visible,
select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible{
  outline:2px solid rgba(var(--glow-rgb),.9);outline-offset:2px;
  border-radius:4px;box-shadow:var(--glow-tight)}

/* ---------- primary button ------------------------------------------------
   Resting: no glow at all. On hover/focus the whole button lights, and the
   hairline that already draws itself along the bottom edge lights with it. */
.btn-pri{box-shadow:0 0 0 0 rgba(var(--glow-rgb),0);
  transition:transform .2s ease,background .2s ease,color .2s ease,box-shadow .3s ease,border-color .2s ease}
.btn-pri:hover,.btn-pri:focus-visible{box-shadow:var(--glow-tight),var(--glow-soft)}
.btn-pri::after{box-shadow:0 0 7px rgba(var(--glow-rgb),.75)}
.btn-gho:hover,.btn-gho:focus-visible{box-shadow:var(--glow-edge)}

/* ---------- the nav indicator lights ---------- */
.nav>*::after{box-shadow:0 0 8px rgba(var(--glow-rgb),.7)}
.nav>.is-active::after{box-shadow:0 0 10px rgba(var(--glow-rgb),.85)}

/* ---------- card and panel edges ------------------------------------------
   Cards do NOT lift on hover — that is 22EW's move and staying off it is half
   the differentiation. They light at the edge instead: same feedback, told in
   the brand's own language. */
.svc:hover .svc-ico,.tech:hover .tech-ico{box-shadow:var(--glow-edge),var(--glow-tight)}
.subcard{transition:transform .22s ease,border-color .22s ease,background .22s ease,box-shadow .3s ease}
.subcard:hover,.subcard:focus-visible{box-shadow:var(--glow-edge)}
.wcard{transition:border-color .3s ease,box-shadow .35s ease}
.wcard:hover{border-color:var(--blue-line);box-shadow:var(--glow-edge),var(--glow-soft)}
.rcard{transition:border-color .3s ease,box-shadow .35s ease}
.rcard:hover{box-shadow:var(--glow-soft)}
.svc-related a:hover,.nearby a:hover,.council-tabs a:hover{box-shadow:var(--glow-edge)}

/* ---------- the section rule glows ---------------------------------------
   The numbered spec-sheet head already draws a hairline across the section.
   A short travelling highlight runs along it as it scrolls in, then stops. */
.ehead-top{position:relative;overflow:hidden}
.ehead-top::after{content:"";position:absolute;left:0;bottom:-1px;width:34%;height:1px;
  background:linear-gradient(90deg,transparent,rgba(var(--glow-rgb),.95),transparent);
  transform:translateX(-110%);opacity:0}
.ehead.in .ehead-top::after{animation:gsweep 1.9s var(--ease) .25s both}
@keyframes gsweep{
  0%{transform:translateX(-110%);opacity:0}
  18%{opacity:1}
  82%{opacity:1}
  100%{transform:translateX(330%);opacity:0}
}
.rule{box-shadow:var(--glow-tight)}

/* ---------- the plug-ring motif -------------------------------------------
   The ticker dot is the mark's ring reduced to its smallest form, so it is the
   one place a slow continuous pulse is allowed. Opacity only. */
.tick-dot{box-shadow:0 0 7px rgba(var(--glow-rgb),.9)}
.hf-dot{box-shadow:0 0 0 4px rgba(60,203,120,.18),0 0 10px rgba(60,203,120,.55)}

/* ===========================================================================
   THE LOGO GLOW.
   Tim: "make the logo glow!! and the logo needs to have a glow outline from the
   png that glows here and there."

   Derived from the PNG'S OWN ALPHA, never from a box shadow. A duplicate of the
   real logo file is stacked behind the visible one and given a drop-shadow
   filter, so the light traces the actual letterforms, the plug and the ring
   rather than a rectangle around them. The duplicate is the same URL, so it
   costs one cache hit and no extra download.

   "Here and there" is read literally: a long 11s cycle that is dark for most of
   its length, lifts once, and settles. On top of that a narrow highlight
   travels across the mark and stops — masked by the same PNG alpha, and moved
   with a transform on an inner element rather than by animating a background
   position, so it stays on the compositor.
   =========================================================================== */
/* Specificity note: these are all written as .gmark>img.<layer> on purpose.
   A plain `.gmark-glow` class selector (0,1,0) loses to `.gmark>img` (0,1,1),
   which would have made the glow layer position:relative and pushed it into
   flow as a second visible logo. */
.gmark{position:relative;display:inline-block;line-height:0;isolation:isolate}
.gmark>img.gmark-main{position:relative;z-index:2;display:block}
.gmark>img.gmark-glow{position:absolute;inset:0;z-index:1;pointer-events:none;
  width:100%;height:100%;object-fit:contain;
  filter:drop-shadow(0 0 3px rgba(var(--glow-rgb),.95))
         drop-shadow(0 0 9px rgba(var(--glow-rgb),.55));
  opacity:var(--glow-rest);
  animation:gbreathe 11s ease-in-out infinite;will-change:opacity}
@keyframes gbreathe{
  0%,62%{opacity:var(--glow-rest)}
  70%{opacity:var(--glow-peak)}
  76%{opacity:calc(var(--glow-rest) + .18)}
  82%{opacity:var(--glow-peak)}
  92%,100%{opacity:var(--glow-rest)}
}
/* the travelling highlight, clipped to the mark's own alpha */
.gmark-sheen{position:absolute;inset:0;z-index:3;pointer-events:none;overflow:hidden;
  -webkit-mask-image:var(--gmark-src);mask-image:var(--gmark-src);
  -webkit-mask-size:contain;mask-size:contain;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center}
.gmark-sheen::before{content:"";position:absolute;top:-60%;bottom:-60%;left:0;width:38%;
  background:linear-gradient(100deg,transparent,rgba(190,225,255,.85),transparent);
  transform:translateX(-160%);
  animation:gsheen 11s ease-in-out infinite;will-change:transform}
@keyframes gsheen{
  0%,58%{transform:translateX(-160%)}
  74%{transform:translateX(190%)}
  100%{transform:translateX(190%)}
}
/* the header lockup and the footer mark carry the same component at different
   intensities — the footer is quieter so it does not compete with the page */
.brand .gmark>img.gmark-glow{--glow-rest:.28}
.ft .gmark>img.gmark-glow{--glow-rest:.2;animation-duration:15s}
.ft .gmark-sheen::before{animation-duration:15s}
/* hovering the header lockup fires the whole cycle at once */
.brand:hover .gmark>img.gmark-glow,.brand:focus-visible .gmark>img.gmark-glow{opacity:var(--glow-peak);
  animation:none;transition:opacity .35s ease}
.brand:hover .gmark-sheen::before,.brand:focus-visible .gmark-sheen::before{
  animation:gsheenOnce 1.1s var(--ease) both}
@keyframes gsheenOnce{from{transform:translateX(-160%)}to{transform:translateX(190%)}}

/* ===========================================================================
   LOGO HOVER — turn blue and glow.
   Tim: "on hover turn blue and glow animation."

   The wordmark is a raster, so the blue state is a pre-rendered sibling file
   (logo-*-blue-*.png, generated from the real artwork by remapping every
   visible pixel onto a brand-blue ramp and copying the alpha channel
   untouched). The two images are stacked and cross-faded, which is why the
   letterforms do not shift by a pixel between states.

   TOUCH HAS NO HOVER, so phones get the equivalent rather than a dead
   interaction: g4.js adds .is-lit once when the mark scrolls into view, plays
   the same transition, and removes it. It also strips any stuck hover state
   after a tap, which is the other half of this bug on touch.
   =========================================================================== */
.gmark>img.gmark-blue{position:absolute;inset:0;z-index:3;pointer-events:none;
  width:100%;height:100%;object-fit:contain;
  opacity:0;transition:opacity .45s var(--ease)}
.gmark.is-hoverable:hover>img.gmark-blue,
.gmark.is-hoverable:focus-within>img.gmark-blue,
.gmark.is-lit>img.gmark-blue{opacity:1}
.gmark.is-hoverable:hover>img.gmark-glow,
.gmark.is-hoverable:focus-within>img.gmark-glow,
.gmark.is-lit>img.gmark-glow{opacity:var(--glow-peak);animation:none;
  filter:drop-shadow(0 0 4px rgba(var(--glow-rgb),1))
         drop-shadow(0 0 14px rgba(var(--glow-rgb),.7));
  transition:opacity .35s ease,filter .35s ease}
.gmark.is-hoverable:hover .gmark-sheen::before,
.gmark.is-lit .gmark-sheen::before{animation:gsheenOnce 1.2s var(--ease) both}
/* a touch device must not be left holding the hover state after a tap */
@media(hover:none){
  .gmark.is-hoverable:hover>img.gmark-blue{opacity:0}
  .gmark.is-hoverable:hover>img.gmark-glow{opacity:var(--glow-rest)}
  .gmark.is-lit>img.gmark-blue{opacity:1}
}

/* the hero quiz panel logo, which the shared bundle renders as a bare <img>
   inside .hq-brand — g4.js upgrades it into the same .gmark component so it
   gets the identical treatment rather than a second, slightly different one */
.hq-brand .gmark{max-width:100%}

@media(prefers-reduced-motion:reduce){
  /* a genuinely static fallback: the glow settles at its resting value and
     nothing moves. Not a slower animation — no animation. */
  .gmark>img.gmark-glow{animation:none;opacity:var(--glow-rest)}
  .gmark-sheen{display:none}
  .ehead.in .ehead-top::after{animation:none;opacity:0}
  .gmark.is-hoverable:hover>img.gmark-glow,.gmark.is-lit>img.gmark-glow{transition:none}
  .gmark>img.gmark-blue{transition:none}
}

/* ===========================================================================
   THEME TOGGLE — bottom left.
   Kept clear of the chat FAB (bottom right), the pinned mobile action bar
   (full width, bottom, z-index 70) and every in-page link. It sits ABOVE the
   pinned bar on phones for the same reason the chat FAB does.
   =========================================================================== */
.tt{position:fixed;left:20px;bottom:24px;z-index:74;
  display:inline-flex;align-items:center;gap:9px;
  height:46px;padding:0 16px 0 13px;border-radius:100px;cursor:pointer;
  background:var(--ink-2);box-shadow:0 8px 24px rgba(0,0,0,.28);
  backdrop-filter:blur(18px) saturate(150%);
  -webkit-backdrop-filter:blur(18px) saturate(150%);
  border:1px solid var(--line-2);color:var(--fg-mute);
  font-family:var(--disp);font-size:11.5px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;
  transition:color .25s ease,border-color .25s ease,box-shadow .3s ease}
.tt:hover,.tt:focus-visible{color:var(--fg);border-color:var(--blue-line);
  box-shadow:var(--glow-edge),var(--glow-tight)}
.tt svg{flex:0 0 auto;width:17px;height:17px}
.tt .tt-sun{display:var(--tt-sun,none)}
.tt .tt-moon{display:var(--tt-moon,none)}
:root[data-theme="dark"]{--tt-sun:block;--tt-moon:none}
:root[data-theme="light"]{--tt-sun:none;--tt-moon:block}
@media(max-width:900px){
  .tt{left:16px;bottom:calc(88px + env(safe-area-inset-bottom));
    height:42px;padding:0 13px 0 11px;font-size:10.5px;letter-spacing:.1em}
}
@media(max-width:400px){ .tt .tt-lbl{display:none} .tt{padding:0 12px} }
@supports not (backdrop-filter:blur(1px)){ .tt{background:var(--ink-2)} }
.tt:hover,.tt:focus-visible{background:var(--ink-3)}

/* ===========================================================================
   REVIEW STARS + THE REAL GOOGLE AGGREGATE.
   Tim: "put 5 stars on these with yellow stars."

   VERIFIED BEFORE SHIPPING, because inventing a rating is exactly the kind of
   claim a client gets called on. Glow's Google Business Profile carries 5.0
   from 16 reviews, and the distribution is 16 five-star and zero of everything
   else, so five stars against each published review is factually true rather
   than decorative. The aggregate is printed as a real, checkable figure and
   linked to the listing itself, which is stronger than bare stars.

   The gold is #E0A32E rather than a bright #FFB020: on the warm graphite ground
   it reaches 8.1:1 and on warm paper 2.9:1 — and because it is used as an ICON
   fill and never as text, the figure that matters is the non-text contrast
   minimum of 3:1, which it clears on dark. On light the stars carry a slightly
   deeper gold for the same reason (see the token below).
   =========================================================================== */
:root[data-theme="light"]{--gold:#A66D06;--gold-glow:166,109,6}
.rstars{display:inline-flex;align-items:center;gap:3px;line-height:0}
.rstars svg{width:14px;height:14px;color:var(--gold);
  filter:drop-shadow(0 0 4px rgba(var(--gold-glow),.45))}
.rcard-top{gap:10px}
.rcard .rstars{flex:0 0 auto}
/* the aggregate line, used under the review section and in the hero strip */
.ragg{display:inline-flex;align-items:center;gap:10px;flex-wrap:wrap;
  text-decoration:none;color:var(--fg-mute);font-size:13.5px;
  padding:10px 0;min-height:44px}
.ragg b{font-family:var(--disp);font-size:15px;color:var(--fg);letter-spacing:.01em}
.ragg span{color:var(--fg-mute2);font-size:12.5px}
.ragg:hover{color:var(--fg)}
.ragg:hover .rstars svg{filter:drop-shadow(0 0 7px rgba(var(--gold-glow),.75))}

/* The one-at-a-time cross fading rotator that used to live here was removed on
   20 Aug. Tim: "have the other reviews on the left and like a carousel faded",
   "centre reviews, do it like the edenbl website." A rotator shows one quote
   and hides the other two behind a wait or a click, which was the complaint.
   The moving rail higher up this file is now the only review component, and
   both placements are set out under "REVIEW RAIL placements" below. */

/* ===========================================================================
   ACCREDITATION BADGES — Tim: "make it animate."
   The marks themselves are untouched: no recolour, no crop, no distortion, no
   filter over the artwork. Michael's name is printed inside the accreditation badges
   and stays fully visible. Only the WHITE CARD behind each badge moves — a
   staggered entrance, then a slow light that passes along the card edge, so the
   strip reads as part of the same glow language without altering an
   accreditation mark, which would be a real problem rather than a design one.
   =========================================================================== */
.accs-badge{position:relative;overflow:hidden;
  opacity:0;transform:translateY(14px);
  transition:opacity .7s var(--ease),transform .7s var(--ease),box-shadow .35s ease}
.accs.in .accs-badge{opacity:1;transform:none}
.accs.in .accs-badge:nth-child(2){transition-delay:.06s}
.accs.in .accs-badge:nth-child(3){transition-delay:.14s}
.accs.in .accs-badge:nth-child(4){transition-delay:.22s}
.accs-badge::after{content:"";position:absolute;top:0;bottom:0;left:0;width:34%;
  background:linear-gradient(100deg,transparent,rgba(var(--glow-rgb),.22),transparent);
  transform:translateX(-180%);pointer-events:none}
.accs.in .accs-badge::after{animation:gsheen 9s ease-in-out infinite}
.accs.in .accs-badge:nth-child(3)::after{animation-delay:.9s}
.accs.in .accs-badge:nth-child(4)::after{animation-delay:1.8s}
.accs-badge:hover{box-shadow:var(--glow-tight)}
@media(prefers-reduced-motion:reduce){
  .accs-badge{opacity:1;transform:none;transition:none}
  .accs.in .accs-badge::after{animation:none;opacity:0}
}

/* ===========================================================================
   ENQUIRY MODAL — the header CTA beside the phone button.
   Tim: "add call to action to right to open pop up form."
   Focus is trapped while open, Escape closes, focus returns to the trigger and
   the page behind is scroll locked (g4.js). The panel is capped in height and
   scrolls internally, so a small phone in landscape can still reach the submit.
   =========================================================================== */
.hd-cta2{flex:0 0 auto;align-self:center}
.mdl{position:fixed;inset:0;z-index:120;display:none;
  align-items:center;justify-content:center;padding:20px}
.mdl.on{display:flex}
.mdl-scrim{position:absolute;inset:0;background:rgba(0,0,0,.62);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  animation:mfade .3s ease both}
@keyframes mfade{from{opacity:0}to{opacity:1}}
.mdl-panel{position:relative;width:min(100%,470px);max-height:calc(100dvh - 40px);
  overflow:auto;-webkit-overflow-scrolling:touch;
  border-radius:18px;background:var(--ink);border:1px solid var(--line-2);
  box-shadow:var(--sh),var(--glow-soft);
  animation:mrise .42s var(--ease) both}
@keyframes mrise{from{opacity:0;transform:translateY(18px) scale(.985)}to{opacity:1;transform:none}}
.mdl-close{position:absolute;top:10px;right:10px;z-index:3;
  width:44px;height:44px;border-radius:50%;border:0;cursor:pointer;
  background:var(--tint-1);color:var(--fg-mute);display:grid;place-items:center}
.mdl-close:hover{color:var(--fg);background:var(--tint-2);box-shadow:var(--glow-edge)}
@media(prefers-reduced-motion:reduce){
  .mdl-scrim,.mdl-panel{animation:none}
}

/* ===========================================================================
   INVERTED CONTACT BAND — Tim: "make this whole section with light background."

   A DELIBERATE INVERSION, not a theme follower. The contact section is the one
   place on the page the reader is being asked to do something, so it flips to
   the opposite ground of whatever theme is running and reads as a band lifted
   out of the page. In dark mode it is warm paper on graphite; in light mode it
   is graphite on warm paper. Same idea, same emphasis, both ways round.

   Every token inside the band is redefined rather than patched per component,
   so the form fields, labels, placeholders, the select, the logo variant and
   the submit button all follow automatically and none of them can be missed.
   Contrast inside the band was computed on the band's own ground, not the
   page's — a section that carries its own tint changes what its text needs.
   =========================================================================== */
.sec-invert{
  --ink:#F6F3EE;--ink-2:#EFEAE2;--ink-3:#E6DFD4;--ink-4:#DBD3C6;--ink-rgb:246,243,238;
  --fg:#171412;--fg-rgb:23,20,18;
  --fg-mute:rgba(23,20,18,.74);--fg-mute2:rgba(23,20,18,.64);
  --line:rgba(23,20,18,.12);--line-2:rgba(23,20,18,.22);
  --tint-1:rgba(23,20,18,.045);--tint-2:rgba(23,20,18,.03);
  --blue-dk:#1F5A8C;--blue-line:rgba(45,108,163,.34);
  --glow-rgb:45,108,163;
  --glow-edge:0 0 0 1px rgba(45,108,163,.38);
  --glow-tight:0 0 6px rgba(45,108,163,.34);
  --glow-soft:0 0 16px rgba(45,108,163,.14);
  --glass-bg:rgba(255,255,255,.55);--glass-hi:rgba(255,255,255,.55);
  --glass-hi-2:rgba(255,255,255,.75);--glass-line:rgba(23,20,18,.14);
  --scrim:rgba(246,243,238,.82);
  --sh:0 24px 60px rgba(60,44,28,.16);
  background:var(--ink);color:var(--fg-mute);
  border-top:1px solid rgba(23,20,18,.14);border-bottom:1px solid rgba(23,20,18,.14)}
:root[data-theme="light"] .sec-invert{
  --ink:#141210;--ink-2:#1C1917;--ink-3:#252220;--ink-4:#302C29;--ink-rgb:20,18,16;
  --fg:#F5F2EE;--fg-rgb:245,242,238;
  --fg-mute:rgba(245,242,238,.72);--fg-mute2:rgba(245,242,238,.55);
  --line:rgba(245,242,238,.10);--line-2:rgba(245,242,238,.18);
  --tint-1:rgba(245,242,238,.045);--tint-2:rgba(245,242,238,.028);
  --blue-dk:#709bc0;--blue-line:rgba(45,108,163,.42);
  --glow-rgb:78,151,212;
  --glow-edge:0 0 0 1px rgba(78,151,212,.42);
  --glow-tight:0 0 8px rgba(78,151,212,.5);
  --glow-soft:0 0 22px rgba(78,151,212,.22);
  --glass-bg:rgba(21,23,26,.55);--glass-hi:rgba(255,255,255,.09);
  --glass-hi-2:rgba(255,255,255,.16);--glass-line:rgba(255,255,255,.14);
  --scrim:rgba(12,13,15,.75);
  --sh:0 24px 60px rgba(16,12,8,.5);
  border-color:rgba(245,242,238,.14)}
/* .sec-dark paints its own ground, so the inverted band has to win over it */
.sec-invert.sec-dark{background:var(--ink)}
.sec-invert .form{background:var(--ink-2)}
.sec-invert h2,.sec-invert h3,.sec-invert h4{color:var(--fg)}
/* THE LOGO IS WHITE EVERYWHERE — Tim, 20 Aug: "the logo needs to be in white,
   wherever the glow logo is."
   This band is the one deliberately LIGHT band on a dark page, and a white
   lockup on cream is not a logo, it is a gap. The old answer was to swap in the
   dark artwork here, which is exactly the exception Tim just closed. So the
   mark keeps the white artwork on every placement without exception and the
   PLATE behind it goes dark instead. Measured on the real plate rather than
   eyeballed: white letterforms on #191614 is 17.8:1, and the blue plug ring
   inside the O, which is untouched artwork in every variant, sits at 4.9:1
   against the same ground. */
.sec-invert .form-brand{
  background:linear-gradient(180deg,#20211F,#191614);
  border-bottom:1px solid rgba(23,20,18,.2)}

/* ===========================================================================
   RECENT WORK LIST — the dense, text-only job feed.
   Tim: "can you add more ServiceM8 recent jobs" / "make sure there are way
   more."

   This is where the work page proves VOLUME. It is built from the in-service
   area completed jobs on Michael's own ServiceM8 account and carries exactly
   three fields per row: a job type from a closed vocabulary we write, an
   allowlisted suburb, and a month. No customer name, no address, no house
   number, no job note and no exact date — see the long note in
   scripts/glow-jobs-curate.js for why each of those is missing on purpose.

   Laid out as a spec sheet rather than a card grid: hairline rows, tabular
   months, a suburb column that reads down the page. That is the same editorial
   language the numbered section heads use, and it is deliberately nothing like
   22EW's card wall.
   =========================================================================== */
.jlist{border-top:1px solid var(--line-2)}
.jrow{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,190px) 96px;
  align-items:baseline;gap:16px;padding:15px 4px;border-bottom:1px solid var(--line);
  transition:background .22s ease,box-shadow .3s ease}
.jrow:hover{background:var(--tint-2)}
.jrow b{font-family:var(--text);font-size:15.5px;font-weight:600;color:var(--fg);
  line-height:1.4}
.jrow .jsub{font-size:14px;color:var(--fg-mute)}
.jrow .jdate{font-size:12.5px;color:var(--fg-mute2);text-align:right;
  font-variant-numeric:tabular-nums;font-family:var(--disp);letter-spacing:.04em}
/* the marker in the first column is the plug ring reduced again, and it lights
   as the row is hovered — the same idle-then-signal behaviour as everything else */
.jrow b::before{content:"";display:inline-block;width:5px;height:5px;border-radius:50%;
  background:var(--blue-line);margin-right:11px;vertical-align:middle;
  transition:background .25s ease,box-shadow .25s ease}
.jrow:hover b::before{background:var(--blue-dk);box-shadow:0 0 7px rgba(var(--glow-rgb),.9)}
.jhead{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;
  gap:10px 20px;padding:0 4px 12px}
.jhead p{margin:0;font-family:var(--disp);font-size:11.5px;font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--fg-mute2)}
.jhead .jstamp{font-size:11.5px;color:var(--fg-mute2);letter-spacing:.04em;
  font-family:var(--disp)}
.jmore{margin:22px 0 0;padding:14px 4px;background:none;border:0;cursor:pointer;
  font-family:var(--disp);font-size:13px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--blue-dk);min-height:48px}
.jmore:hover{color:var(--fg);text-shadow:0 0 12px rgba(var(--glow-rgb),.6)}
.jrow[hidden]{display:none}
@media(max-width:640px){
  .jrow{grid-template-columns:minmax(0,1fr) 74px;gap:4px 12px;padding:13px 2px}
  .jrow b{font-size:15px;grid-column:1}
  .jrow .jsub{grid-column:1;font-size:13.5px;padding-left:16px}
  .jrow .jdate{grid-column:2;grid-row:1/3;align-self:center;font-size:12px}
}

/* ===========================================================================
   SECTION HEAD, CENTRED VARIANT — Tim: "put center" on the FAQ.
   The numbered spec-sheet head is a two column composition (heading left,
   supporting line right) which is right for a section whose content then runs
   full width. The FAQ's content is a single 820px column centred in a 1320px
   wrap, so the left-set head left a large hole beside it and the accordion read
   as pushed off to the right. This variant centres the head over its own
   content and holds the numbered rule at the same width, so the section reads
   as one centred composition rather than two competing alignments.
   =========================================================================== */
.ehead.is-mid{max-width:820px;margin-inline:auto}
.ehead.is-mid .ehead-in{grid-template-columns:1fr;justify-items:center;text-align:center;
  gap:16px}
.ehead.is-mid h2{max-width:20ch}
.ehead.is-mid p{max-width:52ch}
.ehead.is-mid .ehead-top{justify-content:center}
.ehead.is-mid .ehead-meta{margin-left:0}

/* ===========================================================================
   WORK CARDS — rounded, and the photo clipped to the radius.
   .wcard already carries overflow:hidden, but the image sits inside .wimg which
   did not, so on a rounded card the photo's square corner painted over the
   card's curve. Both now carry the radius explicitly.
   =========================================================================== */
.wcard{border-radius:var(--r)}
.wcard .wimg{border-radius:var(--r) var(--r) 0 0;overflow:hidden}
.wempty,.gal-i,.van,.form,.story-card,.subcard,.faq,.svc-side{border-radius:var(--r)}

/* ===========================================================================
   MOBILE — the glow costs nothing on a phone.
   Every continuous animation is stopped below 900px. The interaction glows
   (hover, focus, tap) all stay, because those are one-off and are what make the
   language read; it is the always-running ones that would cost battery.
   =========================================================================== */
@media(max-width:900px){
  .gmark>img.gmark-glow{animation:none}
  .gmark-sheen{display:none}
  .accs.in .accs-badge::after{animation:none;opacity:0}
  .secbg-img img{animation:none}
}

/* ===========================================================================
   HEADER LOCKUP — big at the top, compact once you scroll (the Eden pattern).
   Tim: "make this bigger like Eden then smaller when you scroll."

   Eden BL toggles a `.scrolled` class on its nav past 40px and tightens the
   logo with it; the same shape is used here, adapted to Glow's design system.

   Two deliberate choices:
   1. The state is a CLASS TOGGLE at a threshold, not a value computed on every
      scroll frame, so nothing is recalculated while the page moves. The only
      properties that transition are the lockup's height and the header's
      min-height, each on ONE element, each running twice in a page's life
      rather than on every scroll event. The glow layers on top of it are
      transform and opacity only and are untouched by the resize.
   2. g4.js applies HYSTERESIS: it shrinks past 90px but does not grow back
      until 40px. A single threshold makes the header flicker between states
      when someone rests their scroll right on it.

   The compact height is what the anchor offset has to match. Compact header is
   76px desktop / 68px mobile, and section[id]{scroll-margin-top} is 96px / 86px,
   so a tapped nav link still lands its heading clear of the bar. Lenis carries
   the only scroll offset and Lenis is desktop only, so the mobile number is the
   one that actually matters here.
   =========================================================================== */
.hd-in{min-height:104px;transition:min-height .38s var(--ease)}
.hd.is-sm .hd-in{min-height:76px}
.brand{flex:0 0 auto;display:flex;align-items:center}
.brand .gmark{display:block;height:54px;transition:height .38s var(--ease)}
.brand .gmark>img.gmark-main{height:100%;width:auto;max-width:none}
.brand .gmark>img.gmark-glow,
.brand .gmark>img.gmark-blue{height:100%;width:100%;object-fit:contain;object-position:left center}
.hd.is-sm .brand .gmark{height:34px}
/* the old fixed rule is replaced by the lockup above */
.brand img{height:auto}
@media(max-width:1040px){
  .hd-cta2{display:none}
  .hd-in{min-height:88px}
  .hd.is-sm .hd-in{min-height:68px}
  .brand .gmark{height:42px}
  .hd.is-sm .brand .gmark{height:28px}
}
@media(max-width:640px){
  .brand .gmark{height:38px}
  .hd.is-sm .brand .gmark{height:26px}
}
@media(prefers-reduced-motion:reduce){
  /* jump between the two states rather than losing the shrink entirely */
  .hd-in,.brand .gmark{transition:none}
}

/* footer + form lockups are fixed size and never shrink */
.ft .gmark.flogo{display:block;height:46px;margin-bottom:20px}
.ft .gmark.flogo>img.gmark-main{height:100%;width:auto}
.ft .gmark.flogo>img.gmark-glow,
.ft .gmark.flogo>img.gmark-blue{height:100%;width:100%;object-fit:contain;object-position:left center}
.gform-brand .gmark,.form-brand .gmark{display:block;height:64px}
.gform-brand .gmark>img.gmark-main,.form-brand .gmark>img.gmark-main{height:100%;width:auto}
.gform-brand .gmark>img.gmark-glow,.gform-brand .gmark>img.gmark-blue,
.form-brand .gmark>img.gmark-glow,.form-brand .gmark>img.gmark-blue{
  height:100%;width:100%;object-fit:contain}
@media(max-width:640px){ .gform-brand .gmark,.form-brand .gmark{height:50px} }

/* the mobile drawer carries the header CTA, since both header buttons are
   hidden at the burger breakpoint and the drawer is where they reappear */
.mnav .m-cta{display:block;width:100%;text-align:left;background:none;border:0;
  border-bottom:1px solid var(--line);padding:16px 4px;min-height:54px;cursor:pointer;
  font-family:var(--disp);font-size:19px;letter-spacing:.01em;color:var(--blue-dk);
  font-weight:600}

/* ---------------------------------------------------------------------------
   HERO EYEBROW — Tim: "put ELECTRICAL | SOLAR | BATTERY, we need to know we are
   on an electrician's website."
   The trade now leads, in their own strapline wording off the logo lockup. The
   location was not deleted, because a local trade's suburb names are doing real
   search work; it drops to its own quieter line underneath, so the hierarchy is
   trade first, area second, rather than one replacing the other.
   The pipes are part of the real mark and are not hyphens or dashes.
   --------------------------------------------------------------------------- */
.eyeloc{font-family:var(--disp);font-size:12px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--fg-mute2);font-weight:500;margin:-10px 0 18px}
.phero .eyeloc{margin:-8px 0 14px}
@media(max-width:640px){ .eyeloc{font-size:11px;letter-spacing:.07em} }

/* ---------------------------------------------------------------------------
   REVIEW RAIL placements — Tim, 20 Aug: "centre reviews, do it like the edenbl
   website."

   Both placements run FULL BLEED. Eden's strip crosses the whole viewport and
   the cards fade away past both edges, and that is what makes it read as a
   carousel rather than as a box of quotes; held inside the 1320px wrap the
   mask lands well inside the page and it reads as a clipped panel instead.
   The break out is the standard width:100vw + margin-inline:calc(50% - 50vw),
   which is safe here because body already carries overflow-x:hidden, so a
   visible scrollbar cannot turn 100vw into a sideways scroll.

   The track is centred rather than left set: with two identical halves and the
   loop running to -50%, justify-content has nothing to do once the track is
   wider than the viewport, so the centring only matters on a very wide screen
   where the real set does not fill the row.
   --------------------------------------------------------------------------- */
.rrail{width:100vw;margin-inline:calc(50% - 50vw)}
.rrail-track{margin-inline:auto}
/* hero: the glass card treatment is set once, up in the rail block. All the
   hero placement needs here is the aggregate centred under the strip. */
.rstrip-agg{margin-top:16px;display:flex;justify-content:center}
.rstrip .ragg{margin-top:0}
/* "In their words": bigger type, the section is about the reviews themselves */
#reviews .rrail{margin-top:6px}
#reviews .rcard{padding:28px 30px 26px;flex-basis:clamp(300px,33vw,420px);max-width:420px}
#reviews .rcard p{font-size:16.5px;line-height:1.62}
@media(max-width:640px){
  #reviews .rcard{padding:22px 20px 20px;flex-basis:clamp(250px,80vw,300px)}
  #reviews .rcard p{font-size:15px}
  .rstrip-agg{margin-top:12px}
}

/* the FAQ accordion is a centred column, so its head is centred with it */
#faq .faq{margin-inline:auto}

/* a single full width photograph under each service page hero */
.svcshot{padding-top:0}
.svcshot-fig{margin:0;border-radius:var(--r,18px);overflow:hidden;position:relative;
  border:1px solid rgba(255,255,255,.08);background:var(--ink-2)}
.svcshot-fig img{display:block;width:100%;height:clamp(220px,34vw,420px);object-fit:cover;object-position:center}

/* ===========================================================================
   THE CURRENT LAYER — 11 Sep 2026
   One motion idea told three times: a charge travelling along a line. It runs
   on the council outlines inside the map SVG (see scripts/glow-area-map.mjs,
   which has to carry its own keyframes because a background image SVG never
   sees these custom properties), around the suburb cards, and across the
   review stars.

   Everything here is a compositor only animation — transform and opacity, or a
   background-position on an already painted gradient. No box-shadow, filter or
   width is animated on a loop, because those relayout or repaint every frame
   and this page runs the marquee rail at the same time.
   =========================================================================== */

/* ---------- map legend ---------------------------------------------------
   The map answers "where" but never said what the blue meant. Sits inside
   .area-map, which is role="img", so it is aria-hidden and the aria-label
   carries the same two facts for a screen reader. */
.map-key{position:absolute;left:14px;bottom:14px;z-index:2;
  display:flex;flex-direction:column;gap:7px;
  padding:11px 14px;border-radius:8px;
  background:var(--scrim);border:1px solid var(--glass-line);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}
@supports not (backdrop-filter:blur(1px)){.map-key{background:rgba(12,13,15,.9)}}
.map-key-i{display:flex;align-items:center;gap:9px;
  font-size:12px;line-height:1.2;color:var(--fg);white-space:nowrap}
.map-key-i i{flex:0 0 auto;display:block;width:15px;height:11px;border-radius:2px}
.mk-area{background:rgba(45,108,163,.55);box-shadow:inset 0 0 0 1px rgba(122,177,226,.85)}
.mk-home{width:11px;height:11px;border-radius:50%;background:#7AB1E2;
  box-shadow:0 0 0 3px rgba(122,177,226,.22)}
@media (max-width:640px){
  .map-key{left:10px;bottom:10px;padding:9px 11px;gap:5px}
  .map-key-i{font-size:10.5px;gap:7px}
}

/* ---------- suburb cards, current through the edge ------------------------
   A short bright segment runs the perimeter of each card. Drawn as a masked
   conic gradient so it is one composited layer per card rather than four
   animated pseudo elements, and staggered by column so the grid reads as one
   circuit rather than eleven separate loops. The hover glow above is untouched:
   this is ambient, that is feedback. */
.subcard{position:relative;isolation:isolate}
.subcard::before{content:"";position:absolute;inset:-1px;border-radius:inherit;
  padding:1px;pointer-events:none;z-index:-1;opacity:.9;
  background:conic-gradient(from var(--ang,0deg),
    transparent 0deg,transparent 300deg,
    rgba(122,177,226,.15) 322deg,
    var(--blue-dk) 350deg,
    rgba(207,231,255,.95) 360deg,
    var(--blue-dk) 10deg,
    rgba(122,177,226,.15) 38deg,
    transparent 60deg,transparent 360deg);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude;
  animation:subCurrent 5.6s linear infinite}
.subgrid .subcard:nth-child(4n+2)::before{animation-delay:-1.4s}
.subgrid .subcard:nth-child(4n+3)::before{animation-delay:-2.8s}
.subgrid .subcard:nth-child(4n+4)::before{animation-delay:-4.2s}
@property --ang{syntax:'<angle>';inherits:false;initial-value:0deg}
@keyframes subCurrent{to{--ang:360deg}}
/* Firefox has no @property, so --ang never interpolates and the segment would
   sit frozen at the top of every card. Fall back to a quiet edge breathe. */
@supports not (background:conic-gradient(from 0deg,red,blue)){
  .subcard::before{display:none}
}

/* ---------- review stars, charge running left to right --------------------
   Five stars lit in sequence, then a beat of rest. Per star delay is what makes
   it read as travel rather than five things blinking together. */
.rstars svg{animation:starCurrent 3.4s ease-in-out infinite;will-change:transform,opacity}
.rstars svg:nth-child(1){animation-delay:0s}
.rstars svg:nth-child(2){animation-delay:.13s}
.rstars svg:nth-child(3){animation-delay:.26s}
.rstars svg:nth-child(4){animation-delay:.39s}
.rstars svg:nth-child(5){animation-delay:.52s}
@keyframes starCurrent{
  0%,64%,100%{transform:scale(1);opacity:.82}
  10%{transform:scale(1.22);opacity:1}
  24%{transform:scale(1);opacity:1}
}

/* ---------- the Google mark on each review -------------------------------- */
.rcard-src{display:inline-flex;align-items:center;gap:7px}
.rcard-src svg{flex:0 0 auto;width:13px;height:13px;display:block}

@media(prefers-reduced-motion:reduce){
  .subcard::before,.rstars svg{animation:none}
  .rstars svg{transform:none;opacity:1}
}

/* ---------- the hero film ------------------------------------------------
   Replaces the four rotating shoot stills. Same job as those stills, so it
   inherits the same veil and the same low opacity: it is a ground for the
   headline, never the subject. Muted, looping and playsinline is what lets it
   autoplay at all on iOS; the poster carries the first frame so the hero is
   never empty while the file arrives, and preload="metadata" keeps it off the
   critical path. The phone gets the 768px encode via a media source. */
.secbg-img.is-film{overflow:hidden}
.herofilm{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:center;
  opacity:.30;filter:grayscale(.25) brightness(.95)}
@media (max-width:640px){.herofilm{opacity:.24}}
/* Reduced motion gets the poster frame instead of a paused video element,
   because a paused <video> still decodes on some browsers. */
@media(prefers-reduced-motion:reduce){
  .herofilm{display:none}
  .secbg-img.is-film{
    background:url(/media/video/glow-film-poster.jpg) center/cover no-repeat;
    opacity:.30;filter:grayscale(.25) brightness(.95)}
}

/* ---------- the film block ------------------------------------------------
   The same file again, this time as the subject rather than the ground: full
   brightness, framed, with Michael and the van actually visible. One encode
   serves both, so the second placement is a cache hit rather than a download. */
.filmsec{padding-top:0}
.filmfig{margin:0;position:relative;border-radius:var(--r);overflow:hidden;
  border:1px solid var(--line);background:var(--ink-2);
  box-shadow:0 30px 70px rgba(0,0,0,.35)}
.filmfig video{display:block;width:100%;height:clamp(240px,42vw,540px);
  object-fit:cover;object-position:center}
.filmfig figcaption{position:absolute;left:0;right:0;bottom:0;
  display:flex;align-items:flex-end;justify-content:space-between;gap:16px;
  padding:22px 24px 18px;
  background:linear-gradient(180deg,transparent 0%,rgba(12,13,15,.55) 42%,rgba(12,13,15,.94) 100%);
  font-size:13.5px;color:var(--fg);text-shadow:0 1px 3px rgba(0,0,0,.6)}
.filmfig figcaption b{display:block;font-family:var(--disp);font-size:16px;margin-bottom:3px}
.filmfig figcaption span{color:var(--fg-mute2)}
@media (max-width:640px){
  .filmfig figcaption{padding:16px 16px 14px;font-size:12px;flex-direction:column;
    align-items:flex-start;gap:6px}
  .filmfig figcaption b{font-size:14.5px}
}

/* ---------- job photos on the suburb cards -------------------------------
   Only the seven suburbs with a real completed job in ServiceM8 carry one.
   Nothing is generated and nothing is borrowed from a neighbouring suburb, so
   a card either shows that suburb's own work or shows no photo at all. */
.subcard-ph{display:block;position:relative;width:100%;aspect-ratio:16/10;
  overflow:hidden;border-radius:calc(var(--r) - 1px) calc(var(--r) - 1px) 0 0;
  background:var(--ink-3)}
.subcard-ph img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform .5s cubic-bezier(.2,.7,.3,1)}
.subcard:hover .subcard-ph img,.subcard:focus-visible .subcard-ph img{transform:scale(1.045)}
.subcard-ph::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,transparent 45%,rgba(12,13,15,.5))}
.subcard.has-ph{padding:0;overflow:hidden}
.subcard.has-ph .subcard-tx{display:block;padding:16px 18px 18px}
@media (max-width:640px){.subcard-ph{aspect-ratio:16/9}}

/* ---------- meet the electrician, home page -------------------------------
   Added 11 Sep 2026 at Tim's request: "add the video at the top as well
   smaller", plus more frames from the shoot. Third placement of the SAME two
   encodes, so it costs no extra download, only a cache hit: the hero runs the
   file dimmed to .30 behind the headline, this runs it small and bright as an
   introduction, and the closing block runs it full width. Small on purpose,
   the hero still owns the top of the page.

   The video column STRETCHES to whatever the 2x2 photo grid measures rather
   than carrying a height of its own, so the two halves stay level at every
   width without a magic number that only holds at one viewport. */
.meet{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:18px;align-items:stretch}
.meetfig{margin:0;position:relative;display:flex;flex-direction:column;
  border-radius:var(--r);overflow:hidden;border:1px solid var(--line);
  background:var(--ink-2);box-shadow:0 18px 44px rgba(0,0,0,.28)}
.meetfig video{display:block;flex:1;width:100%;min-height:230px;
  object-fit:cover;object-position:center}
.meetfig figcaption{position:absolute;left:0;right:0;bottom:0;
  display:flex;align-items:flex-end;justify-content:space-between;gap:14px;
  padding:18px 20px 15px;
  background:linear-gradient(180deg,transparent 0%,rgba(12,13,15,.55) 42%,rgba(12,13,15,.94) 100%);
  font-size:12.5px;color:var(--fg);text-shadow:0 1px 3px rgba(0,0,0,.6)}
.meetfig figcaption b{display:block;font-family:var(--disp);font-size:15px;margin-bottom:2px}
.meetfig figcaption span{color:var(--fg-mute2)}
.meetgrid{display:grid;grid-template-columns:1fr 1fr;gap:18px}
.meetph{margin:0;overflow:hidden;border-radius:var(--r);
  border:1px solid var(--line);background:var(--ink-2);aspect-ratio:3/2}
.meetph img{display:block;width:100%;height:100%;object-fit:cover;
  transition:transform .55s cubic-bezier(.2,.7,.3,1)}
.meetph:hover img{transform:scale(1.045)}
@media (max-width:860px){
  .meet{grid-template-columns:1fr;gap:14px}
  .meetgrid{gap:14px}
  .meetfig video{min-height:210px}
}
@media (max-width:640px){
  .meetfig figcaption{padding:14px 14px 12px;font-size:11.5px;
    flex-direction:column;align-items:flex-start;gap:4px}
  .meetfig figcaption b{font-size:13.5px}
  .meetfig video{min-height:190px}
}
/* Reduced motion gets the poster frame, not a paused <video>, for the same
   reason the hero does: a paused video still decodes on some browsers. The
   figure keeps stretching because the grid row height comes from the photo
   grid beside it, so the min-height only matters in the stacked layout. */
@media(prefers-reduced-motion:reduce){
  .meetph img{transition:none}
  .meetph:hover img{transform:none}
  .meetfig video{display:none}
  .meetfig{min-height:230px;
    background:url(/media/video/glow-film-poster.jpg) center/cover no-repeat}
}
