/*
Theme Name: JMP Gym Technologies
Theme URI: https://jmpgymtech.com
Author: Scratch Point Technologies
Description: Marketing site and service platform for JMP Gym Technologies — fitness equipment repair and preventative maintenance, Cochise County AZ.
Version: 1.9.13
Requires at least: 6.4
Requires PHP: 7.4
License: Proprietary
Text Domain: jmp
*/

/* =====================================================
   JMP — GLOBAL TOKENS
   Palette from the logo: carbon black, brushed chrome,
   electric blue.

   Section-level custom properties (--jmp-*) carry their
   DEFAULT values here. inc/design-css.php re-declares
   only the ones an admin has actually changed, scoped to
   [data-jmp-section="..."]. An untouched site emits no
   inline CSS at all.
   ===================================================== */
:root{
  --carbon:#07090d;
  --carbon-2:#0e1218;
  --graphite:#161b24;
  --graphite-2:#1f2631;
  --line:rgba(201,206,214,.12);
  --line-2:rgba(201,206,214,.22);
  --chrome:#c9ced6;
  --chrome-hi:#f2f4f7;
  --chrome-lo:#8a9099;
  --blue:#1f7be6;
  --blue-hi:#5aa8ff;
  --blue-lo:#0b4fb5;
  --blue-glow:rgba(31,123,230,.45);
  --green:#3ddc84;
  --amber:#e8a33d;
  --red:#ef5f5f;
  --white:#fff;

  --display:'Michroma','Exo 2',sans-serif;
  --body:'Exo 2',system-ui,sans-serif;

  --container:1200px;
  --gutter:clamp(20px,5vw,48px);
  --skew:-8deg;
  --radius:6px;
}

/* ---------- Self-hosted fonts ----------
   Served from assets/fonts/ — the brief rules out hotlinking
   Google Fonts.

   Exo 2 ships as a VARIABLE font: one upright file and one
   italic file each cover the whole 100-900 axis, so the
   design's 300/400/600/700/900 weights all come from two
   files rather than five. font-weight declares the supported
   RANGE; the browser instances the weight it needs.

   Each family is split latin / latin-ext with matching
   unicode-range, so the extended file downloads only if the
   page actually uses those glyphs. Michroma is display-only
   (single 400 weight, no italic) — headline caps and eyebrow
   labels; it never needs a second weight. */

/* --- Exo 2 — upright, variable 100-900 --- */
@font-face{
  font-family:'Exo 2';
  src:url('assets/fonts/exo2-var-latin.woff2') format('woff2');
  font-weight:100 900;font-style:normal;font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,
    U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Exo 2';
  src:url('assets/fonts/exo2-var-latin-ext.woff2') format('woff2');
  font-weight:100 900;font-style:normal;font-display:swap;
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,
    U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* --- Exo 2 — italic, variable 100-900 ---
   Carries the black-italic 900 headlines. A real italic cut,
   not a synthesised oblique. */
@font-face{
  font-family:'Exo 2';
  src:url('assets/fonts/exo2-var-italic-latin.woff2') format('woff2');
  font-weight:100 900;font-style:italic;font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,
    U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Exo 2';
  src:url('assets/fonts/exo2-var-italic-latin-ext.woff2') format('woff2');
  font-weight:100 900;font-style:italic;font-display:swap;
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,
    U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* --- Michroma — display, single weight --- */
@font-face{
  font-family:'Michroma';
  src:url('assets/fonts/michroma-400-latin.woff2') format('woff2');
  font-weight:400;font-style:normal;font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,
    U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Michroma';
  src:url('assets/fonts/michroma-400-latin-ext.woff2') format('woff2');
  font-weight:400;font-style:normal;font-display:swap;
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,
    U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* ---------- WordPress admin bar ----------
   The admin bar is fixed to the top of the viewport for logged-in users, so a
   sticky header resting at 0 slides underneath it. This holds the bar's height
   so the sticky offset and the anchor scroll offset both account for it.

   Height and behaviour change with width; these follow WordPress's own
   breakpoints:
     > 782px    32px, fixed
     601-782px  46px, fixed
     <= 600px   46px but position:absolute — it scrolls away with the page, so
                the header must not reserve room for it. */
:root{--jmp-admin-bar:0px}
body.admin-bar{--jmp-admin-bar:32px}
@media screen and (max-width:782px){
  body.admin-bar{--jmp-admin-bar:46px}
}
@media screen and (max-width:600px){
  body.admin-bar{--jmp-admin-bar:0px}
}

/* ---------- Boxed content column ----------
   With "show a separate colour" on, the page paints that colour and each
   content section shrinks to the content width, so the colour shows either
   side and the column reads as deliberate.

   Applies to the content sections only. The header, nav, hero, town ticker,
   closing banner and footer stay edge to edge: their backgrounds are built to
   bleed, and boxing them would crop the hero's chrome sweep and glow, break the
   ticker's continuous scroll, and leave the closing banner floating above a
   full-width footer.

   The width here matches .container's max-width, and the container keeps its
   own padding inside that — so the text column lands in exactly the same place
   it would in full-width mode. Only the background boundary moves. */
body.jmp-boxed{background:var(--jmp-outer-bg)}
body.jmp-boxed .site-main > *:not(.hero):not(.ticker):not(.cta-band){
  max-width:var(--jmp-content-w,1200px);
  margin-inline:auto;
  border-radius:var(--jmp-inset-radius,0px);
  /* Clip the rounding: several sections paint gradients and sweeps that would
     otherwise square off the corners they are meant to follow. */
  overflow:hidden;
}
/* A section already clipping its own overflow keeps doing so; this only adds
   the rounding for the ones that were not. */
body.jmp-boxed .site-main > .pulse-wrap{overflow:visible}

/* ---------- Reset ---------- */
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{
  font-family:var(--body);
  background:var(--carbon);
  color:var(--chrome);
  line-height:1.6;
  font-size:17px;
  -webkit-font-smoothing:antialiased;
  /*
   * No overflow-x:hidden here. Setting it forces overflow-y to compute as
   * auto, which makes body a scroll container — and a position:sticky element
   * then sticks to THAT rather than to the viewport, so it scrolls away as if
   * sticky were never declared. It silently disabled the sticky nav from 1.0.0.
   *
   * It is also unnecessary: every element that reaches past the viewport edge
   * (the hero's chrome sweep, the logo glow, the ticker track, the CTA sheen)
   * sits inside a section that already clips its own overflow. The guard below
   * catches anything new without touching the scroll container.
   */

  /*
   * The nav is pinned, so an anchor jump would land the target's heading
   * underneath it. Offsetting every scroll by the nav height puts the heading
   * just below the bar. Declared here rather than on html because the header
   * template writes --jmp-pinned-h onto body, from the nav height actually in
   * force — so a taller nav moves this with it.
   */
  scroll-padding-top:calc(var(--jmp-pinned-h,78px) + var(--jmp-admin-bar,0px) + 16px);
}
/* Belt and braces: clips stray horizontal overflow from any future section
   without making body a scroll container — overflow:clip, unlike hidden, does
   not create one.

   Deliberately NOT applied to .site-header: that is the sticky nav's ancestor,
   and clipping there would confine it to the header's own box, so it would pin
   briefly and then scroll away with it. */
.site-main,.site-footer{overflow-x:clip}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;cursor:pointer;border:0;background:none;color:inherit}
:focus-visible{outline:2px solid var(--blue-hi);outline-offset:3px}

/* Reduced motion: kill transitions, but scope the marquee override rather
   than letting a blanket reset collapse its duration to ~0 and freeze it. */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms!important;transition-duration:.001ms!important}
  /* Slowed to a crawl rather than frozen: the blanket reset above would
     collapse the duration to ~0 and stop the marquee dead. Derived from the
     configured speed so a slower setting stays slower here too. */
  .ticker-track{animation-duration:calc(var(--jmp-ticker-time,40s) * 4)!important}
}

/* Page width is editable, so the container reads it from a property rather than
   the fixed token. --jmp-content-w and --jmp-gutter-max are set on :root by the
   override engine when they differ from these defaults. */
:root{
  --jmp-content-w:1200px;
  --jmp-gutter-max:48px;
  --jmp-outer-bg:#05070a;
  --jmp-inset-radius:0px;
}
.container{
  max-width:var(--jmp-content-w,var(--container));
  margin:0 auto;
  /* The gutter still shrinks on small screens; the setting caps it. */
  padding-inline:clamp(20px,5vw,var(--jmp-gutter-max,48px));
}
.skip-link{position:absolute;left:-9999px}
.skip-link:focus{left:16px;top:16px;z-index:999;background:var(--blue);color:#fff;padding:12px 18px}

/* ---------- Type ---------- */
h1,h2,h3,h4{font-weight:800;line-height:1.05;color:var(--chrome-hi)}
h2{
  font-family:var(--body);font-style:italic;font-weight:900;
  text-transform:uppercase;letter-spacing:-.01em;
  /* Whole-pixel bounds. A fractional size (3.2rem resolved to 51.2px) makes
     each glyph outline round independently, and a clipped gradient turns that
     rounding into visible letter-to-letter height variation. */
  font-size:clamp(32px,4.2vw,51px);
}
h3{font-family:var(--display);font-size:1rem;letter-spacing:.06em;text-transform:uppercase;line-height:1.3}
p{color:var(--chrome-lo)}
.eyebrow{
  font-family:var(--display);font-size:.68rem;letter-spacing:.28em;text-transform:uppercase;
  color:var(--blue-hi);display:inline-flex;align-items:center;gap:14px;margin-bottom:18px;
}
.eyebrow::before{content:"";width:34px;height:2px;background:linear-gradient(90deg,var(--blue),transparent)}

.chrome{background:linear-gradient(180deg,#fff 0%,#d9dde3 35%,#8d949e 50%,#e6e9ee 62%,#b8bec7 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent}
.blue-text{background:linear-gradient(180deg,#7fbcff 0%,#2b86f0 45%,#0b4fb5 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent}

/* Gradient-clipped text needs a stable rasterisation, and the usual "stabiliser"
   tricks make it worse rather than better:
   - text-rendering:optimizeLegibility switches to inconsistent per-glyph
     subpixel positioning, which reads as letters sitting at different heights
     on a heavy italic at display size.
   - -webkit-text-stroke below 1px rounds per glyph, so some letters get a
     hairline and some do not — a direct source of height variation.
   - will-change:transform permanently promotes every heading to its own
     compositor layer; on a long page that is many layers, and the cost shows
     up as scroll jank.
   All three are gone. What remains is grayscale antialiasing, which keeps glyph
   weight even because it does not vary by subpixel channel, and a plain
   translateZ(0) to settle the paint onto the pixel grid without the permanent
   layer promotion. */
.chrome,.blue-text,.brand-word{
  -webkit-transform:translateZ(0);transform:translateZ(0);
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  /* Sub-pixel font sizes round unevenly across glyphs once a gradient is
     clipped to them; snapping the computed size keeps the baseline flat. */
  font-synthesis:none;
}
/* Explicit "no gradient" span, for headings where only part takes a treatment
   and the rest should stay flat heading colour. */
.plain{color:var(--chrome-hi)}

/* A five-stop clip is noise at small sizes — flat colour reads better. */
.dash-list .chrome,.tab-body .chrome,.faq .chrome{
  background:none;-webkit-background-clip:border-box;background-clip:border-box;
  color:var(--chrome-hi);-webkit-text-stroke:0;
}

/* ---------- Buttons ---------- */
.btn{
  display:inline-flex;align-items:center;gap:10px;
  font-family:var(--display);font-size:.72rem;letter-spacing:.18em;text-transform:uppercase;
  padding:16px 26px;position:relative;
  transform:skewX(var(--skew));
  transition:transform .2s,box-shadow .2s,background .2s;
  white-space:nowrap;
}
.btn > *{transform:skewX(calc(var(--skew) * -1));display:inline-block}
.btn-primary{
  background:linear-gradient(180deg,var(--blue-hi),var(--blue) 45%,var(--blue-lo));
  color:var(--white);
  box-shadow:0 0 0 1px rgba(255,255,255,.12) inset,0 10px 30px -10px var(--blue-glow);
}
.btn-primary:hover{transform:skewX(var(--skew)) translateY(-2px);box-shadow:0 0 0 1px rgba(255,255,255,.2) inset,0 16px 36px -10px var(--blue-glow)}
.btn-chrome{background:linear-gradient(180deg,#f4f6f8,#c5cad2 50%,#9aa1ab);color:#0b0e13}
.btn-chrome:hover{transform:skewX(var(--skew)) translateY(-2px)}
.btn-ghost{color:var(--chrome-hi);box-shadow:0 0 0 1px rgba(201,206,214,.35) inset}
.btn-ghost:hover{box-shadow:0 0 0 1px var(--blue-hi) inset;color:var(--blue-hi)}

/* =====================================================
   HEADER
   Editable: --jmp-topbar-bg --jmp-topbar-dot --jmp-nav-bg
   --jmp-nav-link --jmp-nav-accent --jmp-nav-h
   --jmp-logo-h --jmp-brand-from --jmp-brand-to
   ===================================================== */
[data-jmp-section="header"]{
  --jmp-topbar-bg:#0e1218;
  --jmp-topbar-dot:#3ddc84;
  --jmp-nav-bg:#07090d;
  --jmp-nav-link:#c9ced6;
  --jmp-nav-accent:#5aa8ff;
  --jmp-nav-h:78px;
  --jmp-logo-h:62px;
  --jmp-brand-from:#7fbcff;
  --jmp-brand-to:#0e57bd;

  /*
   * The HEADER is the sticky element, not the nav inside it.
   *
   * A sticky element can only stick within its parent's box, and .site-header
   * is only as tall as the top bar plus the nav — so a sticky .nav held for
   * about 114px and then left with its parent. Removing body's overflow-x in
   * 1.9.2 was necessary but not sufficient; this containing block was the rest
   * of it.
   *
   * Sticking the header with a NEGATIVE top offset equal to the top bar's
   * height lets the bar scroll away while the nav comes to rest against the
   * viewport edge — the behaviour the mockup implied.
   */
  display:block;
  position:sticky;
  top:calc(var(--jmp-pinned-h,78px) - var(--jmp-header-h,114px) + var(--jmp-admin-bar,0px));
  z-index:50;
}

/* --jmp-admin-bar is defined near the top of this file, beside the reset. */

/* The hero subtracts the real header height rather than a fixed 114px, since
   the nav height is editable and the top bar can be switched off. The header
   template writes --jmp-header-h inline from the values actually in force. */

.topbar{
  background:var(--jmp-topbar-bg);
  border-bottom:1px solid var(--line);
  font-size:.78rem;letter-spacing:.06em;
}
.topbar .container{display:flex;justify-content:space-between;align-items:center;height:36px;gap:16px}
.topbar span{color:var(--chrome-lo)}
.topbar strong{color:var(--chrome-hi);font-weight:600}
.topbar .dot{
  display:inline-block;width:7px;height:7px;border-radius:50%;
  background:var(--jmp-topbar-dot);
  box-shadow:0 0 8px var(--jmp-topbar-dot);
  margin-right:8px;vertical-align:middle;
}

.nav{
  /* Not sticky itself — the header above it is. A sticky child cannot escape
     its parent's box, which is what kept this pinned for only ~114px. */
  position:relative;
  /* colour-mix keeps the translucency editable from one hex value */
  background:color-mix(in srgb,var(--jmp-nav-bg) 78%,transparent);
  /* Deliberately no backdrop-filter here — see the rule below. */
  border-bottom:1px solid var(--line);
  transition:box-shadow .3s;
}
/* The blur is applied only for visitors who have not asked for reduced motion
   and are on a device that reports fine pointer input — in practice a desktop
   or laptop rather than a phone doing its best.

   A sticky element with backdrop-filter re-blurs everything scrolling beneath
   it every frame, across the full page width. It is the most expensive thing on
   this page during a scroll and a common cause of stuttering, so it is treated
   as an enhancement rather than part of the design. The nav is legible without
   it: the background sits at 78% opacity either way. */
@media (prefers-reduced-motion:no-preference) and (pointer:fine){
  /* Only while unscrolled: once .scrolled makes the background opaque there is
     nothing to see through, and this is the costliest property on the page
     during a scroll. Dropping it exactly when the page starts moving is where
     it matters most. */
  .nav:not(.scrolled){backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px)}
}
@supports not (background:color-mix(in srgb,#000 50%,transparent)){
  .nav{background:var(--jmp-nav-bg)}
}
/* Once pinned the nav has page content passing behind it, so the translucent
   background becomes fully opaque — at 78% the top bar and then the hero would
   show through it as they scroll past. The shadow separates it from what is
   moving underneath. */
.nav.scrolled{
  background:var(--jmp-nav-bg);
  box-shadow:0 10px 40px -10px rgba(0,0,0,.8);
}
.nav .container{display:flex;align-items:center;justify-content:space-between;height:var(--jmp-nav-h);gap:24px}

.brand{display:flex;align-items:center;gap:15px;min-width:0}
/* The global img{max-width:100%} plus min-width:0 on a flex parent silently
   crushes the logo's aspect ratio even with flex:none. max-width:none is the fix. */
.brand img{height:var(--jmp-logo-h);width:auto;flex:none;max-width:none;object-fit:contain}
.brand-word,.jmp-logo-fallback{
  font-family:var(--body);font-style:italic;font-weight:800;
  font-size:1.4rem;letter-spacing:-.005em;text-transform:uppercase;
  line-height:1;white-space:nowrap;padding-block:2px;
  background:linear-gradient(180deg,var(--jmp-brand-from) 0%,var(--jmp-brand-to) 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
}

.nav-links{display:flex;gap:34px;list-style:none}
.nav-links a{
  white-space:nowrap;font-family:var(--display);font-size:.66rem;
  letter-spacing:.2em;text-transform:uppercase;
  color:var(--jmp-nav-link);position:relative;padding:8px 0;
}
.nav-links a::after{content:"";position:absolute;left:0;bottom:0;height:2px;width:0;background:var(--jmp-nav-accent);transition:width .25s}
.nav-links a:hover::after,.nav-links a.active::after{width:100%}
.nav-cta{display:flex;align-items:center;gap:22px}
.nav-phone{white-space:nowrap;font-weight:700;color:var(--chrome-hi);font-size:.95rem;letter-spacing:.03em}
.nav-phone span{color:var(--jmp-nav-accent)}

.burger{display:none;width:44px;height:44px;flex-direction:column;justify-content:center;gap:6px;align-items:center}
.burger i{display:block;width:24px;height:2px;background:var(--chrome-hi);transition:transform .3s,opacity .3s}
.burger.open i:nth-child(1){transform:translateY(8px) rotate(45deg)}
.burger.open i:nth-child(2){opacity:0}
.burger.open i:nth-child(3){transform:translateY(-8px) rotate(-45deg)}
.mobile-menu{display:none;background:var(--carbon-2);border-bottom:1px solid var(--line)}
.mobile-menu.open{display:block}
.mobile-menu a{
  display:block;padding:18px var(--gutter);
  font-family:var(--display);font-size:.72rem;letter-spacing:.2em;text-transform:uppercase;
  border-top:1px solid var(--line);
}

/* =====================================================
   MAIN — sections land here one chunk at a time
   ===================================================== */
.site-main{display:block;min-height:40vh}
/* Spacing is per-section and editable. The properties default to 110px here, so
   a section that has not declared its own still gets the original rhythm. */
.site-main > section,
.site-main > .brands,
.site-main > .perks,
.site-main > .cta-band,
.site-main > .pulse-wrap{
  padding-block:var(--jmp-space-top,110px) var(--jmp-space-bottom,110px);
  position:relative;
}
.section-head{max-width:760px;margin-bottom:56px}
.section-head p{margin-top:18px;font-size:1.05rem}

/* Placeholder shown until the first content section is built. */
.jmp-empty{
  padding:120px 0;text-align:center;
  border-bottom:1px solid var(--line);
}
.jmp-empty h2{margin-bottom:14px}
.jmp-empty p{max-width:52ch;margin:0 auto}

/* =====================================================
   HERO
   Editable: --jmp-hero-bg --jmp-hero-glow --jmp-hero-glow-op
   --jmp-hero-pat-op --jmp-hero-stat
   ===================================================== */
[data-jmp-section="hero"]{
  --jmp-space-top:80px;
  --jmp-space-bottom:60px;
  --jmp-hero-bg:#07090d;
  --jmp-hero-glow:#1f7be6;
  --jmp-hero-glow-op:22%;
  --jmp-hero-pat-op:100%;
  --jmp-hero-stat:#1f7be6;
  --jmp-hero-height:100vh;
  --jmp-hero-logo-max:520px;
}

.site-main > .hero{
  /* Height is a share of the viewport minus the real header, not a fixed
     figure: the nav height is editable and the top bar can be switched off,
     so a hardcoded offset would leave the hero taller than the screen and
     push the copy off-centre. */
  min-height:calc(var(--jmp-hero-height) - var(--jmp-header-h,114px));
  display:flex;align-items:center;
  /* Spacing comes from --jmp-space-* above. */
  overflow:hidden;
  /* Two glow pools over the flat base — the large one sits behind the logo,
     the small one lifts the lower left so the panel is not evenly dark. */
  background:
    radial-gradient(ellipse 70% 60% at 75% 40%,
      color-mix(in srgb,var(--jmp-hero-glow) var(--jmp-hero-glow-op),transparent),
      transparent 60%),
    radial-gradient(ellipse 50% 40% at 15% 90%,
      color-mix(in srgb,var(--jmp-hero-glow) calc(var(--jmp-hero-glow-op) / 2),transparent),
      transparent 60%),
    var(--jmp-hero-bg);
}
@supports not (background:color-mix(in srgb,#000 50%,transparent)){
  .site-main > .hero{background:var(--jmp-hero-bg)}
}

/* Pattern layer. Masked to a soft ellipse so it reads as texture behind the
   subject rather than wallpaper running to the edges. */
.hero::before{
  content:"";position:absolute;inset:0;
  background-image:var(--pat-img,none);
  background-size:var(--pat-size,auto);
  background-position:var(--pat-pos,0 0);
  opacity:var(--jmp-hero-pat-op);
  mask-image:radial-gradient(ellipse 80% 80% at 60% 40%,#000 30%,transparent 80%);
  -webkit-mask-image:radial-gradient(ellipse 80% 80% at 60% 40%,#000 30%,transparent 80%);
  pointer-events:none;
}

/* Chrome sweep — a single soft highlight raking across the panel. */
.hero::after{
  content:"";position:absolute;right:-20%;top:-40%;width:70%;height:180%;
  background:linear-gradient(115deg,transparent 40%,rgba(255,255,255,.035) 50%,transparent 60%);
  transform:skewX(-12deg);pointer-events:none;
}

.hero .container{
  position:relative;z-index:2;
  display:grid;grid-template-columns:1.15fr .85fr;gap:60px;align-items:center;
}
.hero.no-logo .container{grid-template-columns:1fr;max-width:900px}

.hero h1{
  font-family:var(--body);font-style:italic;font-weight:900;text-transform:uppercase;
  /* Whole-pixel bounds — see the note on h2. 6.8vw rather than 6.4 so the
     whole-pixel maximum wins from 1280px up, which covers every common desktop
     width; below that the fluid value takes over where text is smaller and the
     rounding is not visible. */
  font-size:clamp(42px,6.8vw,86px);
  letter-spacing:-.025em;line-height:.95;
  margin-bottom:26px;
}
.hero h1 .chrome,.hero h1 .blue-text{display:block}

.hero .lead{
  font-size:1.1rem;color:var(--chrome);max-width:62ch;
  font-weight:300;margin-bottom:36px;
}
.hero .lead strong{color:var(--chrome-hi);font-weight:600}

/* Hiding the headline needs no styling of its own: the h1 is simply not
   rendered, so the intro text moves up into that space at its normal size,
   weight and colour. The .no-headline class stays on the section as a hook for
   anything that later needs to know, but nothing is restyled by it. */

.hero-actions{display:flex;flex-wrap:wrap;gap:16px;margin-bottom:44px}
.hero-stats{display:flex;gap:26px;flex-wrap:wrap}
.hero-stats .stat{border-left:2px solid var(--jmp-hero-stat);padding-left:16px}
.hero-stats .stat b{
  display:block;font-family:var(--display);font-size:1.1rem;
  white-space:nowrap;color:var(--chrome-hi);letter-spacing:.04em;
}
.hero-stats .stat span{
  font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;color:var(--chrome-lo);
}

.hero-logo{
  position:relative;
  display:flex;align-items:center;justify-content:center;
  /* The grid row is as tall as the copy column, so this cell can be far taller
     than the logo. Without a cap the flex child stretches to fill it. */
  min-height:0;
}
.hero-logo img{
  width:auto;height:auto;
  max-width:min(100%,var(--jmp-hero-logo-max));
  /* Bound against the viewport as well as the cell: on a tall/narrow window the
     hero's min-height makes the row taller than the image should ever be. */
  max-height:min(var(--jmp-hero-logo-max),60vh);
  object-fit:contain;
  /* A flex child defaults to align-self:stretch, which distorts an image that
     has a width but no height. Centring keeps its intrinsic aspect ratio. */
  align-self:center;flex:0 1 auto;
  filter:drop-shadow(0 30px 60px color-mix(in srgb,var(--jmp-hero-glow) 35%,transparent));
  animation:jmp-float 7s ease-in-out infinite;
}
.hero.no-float .hero-logo img{animation:none}
@keyframes jmp-float{0%,100%{transform:translateY(0)}50%{transform:translateY(-12px)}}
.hero-logo::before{
  content:"";position:absolute;width:75%;aspect-ratio:1;border-radius:50%;
  background:radial-gradient(circle,
    color-mix(in srgb,var(--jmp-hero-glow) 35%,transparent),transparent 65%);
  filter:blur(30px);z-index:-1;
}

/* =====================================================
   TWO PATHS — residential vs commercial
   Editable: --jmp-paths-bg --jmp-paths-accent --jmp-paths-com
   ===================================================== */
[data-jmp-section="paths"]{
  --jmp-space-top:110px;
  --jmp-space-bottom:110px;
  --jmp-paths-bg:#161b24;
  --jmp-paths-accent:#5aa8ff;
  --jmp-paths-com:#1f7be6;
}

.split{
  display:grid;grid-template-columns:1fr 1fr;gap:0;
  border:1px solid var(--line);
  background:var(--jmp-paths-bg);
  position:relative;
}
/* Skewed chrome seam between the two halves, echoing the button skew.
   Centred on its own width so the line sits ON the midpoint rather than
   starting at it, which is what lets the washes below meet it exactly. */
.split::before{
  content:"";position:absolute;left:50%;top:0;bottom:0;width:2px;
  margin-left:-1px;
  background:linear-gradient(180deg,transparent,var(--chrome),transparent);
  transform:skewX(var(--skew));
  z-index:1;pointer-events:none;
}

/* The two washes are painted on the PARENT, split by a gradient cut at the
   same angle as the seam. Applying them per-panel instead left their boundary
   on the grid's vertical column edge while the seam ran at -8deg, so the two
   crossed at the midpoint and visibly diverged above and below it.
   98deg is the vertical 90deg plus the 8deg skew. */
.split::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:
    linear-gradient(98deg,
      rgba(201,206,214,.07) 0 calc(50% - 1px),
      color-mix(in srgb,var(--jmp-paths-com) 10%,transparent) calc(50% + 1px) 100%);
  /* Fade both washes out downward, as the per-panel gradients did. */
  mask-image:linear-gradient(160deg,#000,transparent 60%);
  -webkit-mask-image:linear-gradient(160deg,#000,transparent 60%);
}
@supports not (background:color-mix(in srgb,#000 50%,transparent)){
  .split::after{
    background:linear-gradient(98deg,
      rgba(201,206,214,.07) 0 calc(50% - 1px),
      rgba(31,123,230,.10) calc(50% + 1px) 100%);
  }
}
/* Panels sit above the wash so their text is never dimmed by it. */
.path{position:relative;z-index:2}
.path{padding:52px 48px;position:relative;display:flex;flex-direction:column;gap:22px}
.path .tag{
  font-family:var(--display);font-size:.6rem;letter-spacing:.3em;
  text-transform:uppercase;color:var(--jmp-paths-accent);
}
.path h3{
  font-family:var(--body);font-style:italic;font-weight:900;
  font-size:2rem;text-transform:uppercase;letter-spacing:-.01em;
}
.path ul{list-style:none;display:grid;gap:10px;margin:6px 0 10px}
.path li{display:flex;gap:12px;align-items:flex-start;color:var(--chrome);font-size:.98rem}
.path li::before{
  content:"";flex:none;margin-top:9px;width:10px;height:2px;
  background:var(--jmp-paths-accent);
}
/* Buttons sit flush at the foot of each panel however long the lists are. */
.path > div:last-child{margin-top:auto}

/* =====================================================
   SERVICES — tabbed card grid
   Editable: --jmp-svc-bg --jmp-svc-card --jmp-svc-accent
   --jmp-svc-cols
   ===================================================== */
[data-jmp-section="services"]{
  --jmp-space-top:110px;
  --jmp-space-bottom:110px;
  --jmp-svc-bg:#0e1218;
  --jmp-svc-card:#161b24;
  --jmp-svc-accent:#5aa8ff;
  --jmp-svc-cols:3;
}
.site-main > .services{background:var(--jmp-svc-bg)}

.tabs{display:inline-flex;border:1px solid var(--line);margin-bottom:44px;transform:skewX(var(--skew))}
.tab{
  padding:14px 30px;
  font-family:var(--display);font-size:.66rem;letter-spacing:.22em;text-transform:uppercase;
  color:var(--chrome-lo);transition:background .2s,color .2s;
}
.tab > span{display:inline-block;transform:skewX(calc(var(--skew) * -1))}
.tab[aria-selected="true"]{
  background:linear-gradient(180deg,var(--blue-hi),var(--blue) 45%,var(--blue-lo));
  color:#fff;
}
.services .panel{display:none}
.services .panel.active{display:block;animation:jmp-fade .4s ease}
@keyframes jmp-fade{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

.services .grid{
  display:grid;
  grid-template-columns:repeat(var(--jmp-svc-cols),1fr);
  gap:18px;
}
.services .card{
  background:var(--jmp-svc-card);border:1px solid var(--line);
  padding:32px 28px;position:relative;overflow:hidden;
  display:flex;flex-direction:column;
  transition:transform .25s,border-color .25s;
}
/* Accent rail slides in on hover rather than sitting there permanently. */
.services .card::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:linear-gradient(180deg,var(--jmp-svc-accent),var(--blue-lo));
  opacity:0;transition:opacity .25s;
}
.services .card:hover{
  transform:translateY(-4px);
  border-color:color-mix(in srgb,var(--jmp-svc-accent) 50%,transparent);
}
@supports not (border-color:color-mix(in srgb,#000 50%,transparent)){
  .services .card:hover{border-color:rgba(31,123,230,.5)}
}
.services .card:hover::before{opacity:1}

.services .card .icon{
  width:46px;height:46px;margin-bottom:22px;
  display:grid;place-items:center;flex:none;
  border:1px solid rgba(201,206,214,.25);
  transform:skewX(var(--skew));
  background:linear-gradient(180deg,rgba(255,255,255,.06),transparent);
}
.services .card .icon svg{
  width:22px;height:22px;
  stroke:var(--jmp-svc-accent);fill:none;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;
  transform:skewX(calc(var(--skew) * -1));
}
/* An uploaded image un-skews and fills the tile instead of being stroked. */
.services .card .icon-img{overflow:hidden}
.services .card .icon-img img{
  width:100%;height:100%;object-fit:cover;
  transform:skewX(calc(var(--skew) * -1)) scale(1.15);
}
.services .card h3{margin-bottom:12px;font-size:.92rem}
.services .card p{font-size:.95rem}
.services .card .card-link{
  margin-top:auto;padding-top:16px;
  font-family:var(--display);font-size:.6rem;letter-spacing:.2em;text-transform:uppercase;
  color:var(--jmp-svc-accent);
}
.services .card .card-link:hover{text-decoration:underline}

/* =====================================================
   MAINTENANCE — PM case + plan tiers
   Editable: --jmp-pm-bg --jmp-pm-glow --jmp-pm-glow-op
   --jmp-pm-card --jmp-pm-accent
   ===================================================== */
[data-jmp-section="maintenance"]{
  --jmp-space-top:110px;
  --jmp-space-bottom:110px;
  --jmp-pm-bg:#07090d;
  --jmp-pm-glow:#1f7be6;
  --jmp-pm-glow-op:18%;
  --jmp-pm-card:#161b24;
  --jmp-pm-accent:#5aa8ff;
}
.site-main > .pm{
  overflow:hidden;
  /* Glow sits left, under the argument rather than the tier cards. */
  background:
    radial-gradient(ellipse 60% 70% at 20% 50%,
      color-mix(in srgb,var(--jmp-pm-glow) var(--jmp-pm-glow-op),transparent),
      transparent 60%),
    var(--jmp-pm-bg);
}
@supports not (background:color-mix(in srgb,#000 50%,transparent)){
  .site-main > .pm{background:var(--jmp-pm-bg)}
}
.pm .container{display:grid;grid-template-columns:1fr 1fr;gap:70px;align-items:start}
.pm-intro{margin-top:18px;font-size:1.05rem}

.checklist{list-style:none;display:grid;gap:12px;margin-top:28px}
.checklist li{display:flex;gap:14px;align-items:flex-start;color:var(--chrome)}
.checklist li svg{
  flex:none;width:20px;height:20px;margin-top:3px;
  stroke:var(--jmp-pm-accent);fill:none;
  stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;
}
.checklist li b{color:var(--chrome-hi)}
.pm-cta{margin-top:34px}

.pm-plans{display:grid;gap:14px}
.plan{
  border:1px solid var(--line);background:var(--jmp-pm-card);
  padding:26px 28px;
  display:grid;grid-template-columns:auto 1fr;gap:22px;align-items:center;
  transition:border-color .25s,background .25s;
}
.plan:hover{
  border-color:color-mix(in srgb,var(--jmp-pm-accent) 50%,transparent);
  background:var(--graphite-2);
}
@supports not (border-color:color-mix(in srgb,#000 50%,transparent)){
  .plan:hover{border-color:rgba(31,123,230,.5)}
}
/* Frequency runs vertically up the left edge — it is a label, not a heading,
   and this keeps it out of the reading order of the card body. */
.plan .freq{
  font-family:var(--display);font-size:.62rem;letter-spacing:.2em;text-transform:uppercase;
  color:var(--jmp-pm-accent);
  writing-mode:vertical-rl;transform:rotate(180deg);
  border-right:2px solid var(--jmp-pm-accent);
  padding-right:14px;height:70px;
  display:flex;align-items:center;
}
.plan h4{
  font-family:var(--display);font-size:.85rem;letter-spacing:.06em;
  text-transform:uppercase;margin-bottom:8px;
}
.plan p{font-size:.93rem}
.plan.featured{
  border-color:var(--jmp-pm-accent);
  box-shadow:0 0 0 1px var(--jmp-pm-accent) inset,
             0 20px 50px -20px color-mix(in srgb,var(--jmp-pm-glow) 45%,transparent);
}
@supports not (box-shadow:0 0 0 1px color-mix(in srgb,#000 50%,transparent)){
  .plan.featured{box-shadow:0 0 0 1px var(--jmp-pm-accent) inset,0 20px 50px -20px var(--blue-glow)}
}
.pm-note{font-size:.85rem;letter-spacing:.04em;margin-top:6px}

/* =====================================================
   PROCESS — numbered steps
   Editable: --jmp-proc-bg --jmp-proc-num --jmp-proc-accent
   ===================================================== */
[data-jmp-section="process"]{
  --jmp-space-top:110px;
  --jmp-space-bottom:110px;
  --jmp-proc-bg:#0e1218;
  --jmp-proc-num:#f2f4f7;
  --jmp-proc-accent:#1f7be6;
}
.site-main > .process{background:var(--jmp-proc-bg)}

.steps{
  display:grid;
  /* Column count follows the number of steps rather than assuming four. */
  grid-template-columns:repeat(var(--jmp-proc-count,4),1fr);
  gap:0;position:relative;
}
/* Rule behind the numbers. Inset by half a column at each end so it starts and
   stops at the first and last marker rather than running off the edges. */
.steps::before{
  content:"";position:absolute;top:34px;height:1px;
  left:calc(50% / var(--jmp-proc-count,4));
  right:calc(50% / var(--jmp-proc-count,4));
  background:linear-gradient(90deg,transparent,var(--chrome-lo) 8%,var(--chrome-lo) 92%,transparent);
}
.process.no-connector .steps::before{display:none}

.step{padding:0 28px 0 0;position:relative}
.step .num{
  width:68px;height:68px;display:grid;place-items:center;
  background:var(--jmp-proc-bg);
  border:1px solid var(--chrome-lo);
  font-family:var(--display);font-size:.9rem;color:var(--jmp-proc-num);
  transform:skewX(var(--skew));
  margin-bottom:26px;position:relative;z-index:1;
}
.step .num span{transform:skewX(calc(var(--skew) * -1));display:block}
/* The first step is filled to mark where the sequence starts. */
.step:first-child .num{
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--jmp-proc-accent) 78%,#fff),
    var(--jmp-proc-accent) 45%,
    color-mix(in srgb,var(--jmp-proc-accent) 70%,#000));
  border-color:transparent;color:#fff;
}
@supports not (background:color-mix(in srgb,#000 50%,transparent)){
  .step:first-child .num{background:var(--jmp-proc-accent)}
}
.step h3{margin-bottom:10px;font-size:.85rem}
.step p{font-size:.93rem}

/* =====================================================
   SERVICE AREA — town list, generated map, ticker
   Editable: --jmp-area-map-bg --jmp-area-accent
   --jmp-area-pin --jmp-ticker-time
   ===================================================== */
[data-jmp-section="area"]{
  --jmp-space-top:110px;
  --jmp-space-bottom:110px;
  --jmp-area-map-bg:#161b24;
  --jmp-area-accent:#5aa8ff;
  --jmp-area-pin:#c9ced6;
  --jmp-ticker-time:40s;
}
.area .container{display:grid;grid-template-columns:.9fr 1.1fr;gap:70px;align-items:center}
.area-intro{margin-top:18px;font-size:1.05rem}

.towns{display:grid;grid-template-columns:1fr 1fr;gap:10px 30px;list-style:none;margin-top:28px}
.towns li{
  padding:10px 0;border-bottom:1px solid var(--line);
  display:flex;justify-content:space-between;gap:12px;
  font-size:.95rem;color:var(--chrome);
}
.towns li small{
  color:var(--chrome-lo);font-size:.72rem;
  letter-spacing:.1em;text-transform:uppercase;
}
.area-ask{margin-top:16px;font-size:.92rem;color:var(--chrome-lo)}
.area-ask a{color:var(--jmp-area-accent);font-weight:600}

/* ---------- Generated map ---------- */
.route{
  position:relative;background:var(--jmp-area-map-bg);
  border:1px solid var(--line);padding:28px;
}
.route svg{width:100%;height:auto;display:block;overflow:visible}
.cty-lbl{
  font:600 8.5px var(--body);fill:var(--chrome-lo);letter-spacing:.14em;
  text-transform:uppercase;
}
/* ---- Real geometry drawn beneath the pins ----
   County outline and highway network from OpenStreetMap. Kept low-contrast so
   it reads as context, never competing with the towns themselves. */
.county{
  fill:rgba(31,123,230,.05);
  stroke:rgba(90,168,255,.32);
  stroke-width:1.2;stroke-linejoin:round;
}
.hwy{
  fill:none;
  stroke:rgba(201,206,214,.20);
  stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;
}
/* Interstates read heavier, as they do on a printed road map. */
.hwy.major{
  stroke:color-mix(in srgb,var(--jmp-area-accent) 45%,transparent);
  stroke-width:2;
}
@supports not (stroke:color-mix(in srgb,#000 50%,transparent)){
  .hwy.major{stroke:rgba(90,168,255,.45)}
}
.hwy-badge rect{fill:var(--jmp-area-map-bg);opacity:.92}
.hwy-badge text{
  font:700 7px var(--body);
  fill:var(--chrome-lo);letter-spacing:.04em;
}
.pin{fill:var(--jmp-area-pin)}
.pin-lbl{font:600 9.5px var(--body);fill:var(--jmp-area-pin)}
.pin-hub{fill:var(--jmp-area-accent)}
.pin-hub-ring{
  fill:none;stroke:var(--jmp-area-accent);stroke-width:1.5;opacity:.55;
  transform-origin:center;transform-box:fill-box;
  animation:jmp-ping 2.6s ease-out infinite;
}
/* The map's pulsing ring keeps animating far below the fold. Skipping render
   for off-screen content stops it costing frames during a scroll.

   Applied to the map only, not the hero logo: the hero is above the fold, where
   deferring render would work against first paint rather than for it. */
.route{content-visibility:auto;contain-intrinsic-size:auto 480px}
@keyframes jmp-ping{
  0%{transform:scale(.55);opacity:.75}
  100%{transform:scale(1.6);opacity:0}
}
.pin-lbl-hub{font:800 11px var(--body);fill:var(--chrome-hi)}

.map-key{
  display:flex;gap:22px;margin-top:14px;flex-wrap:wrap;
  font-size:.8rem;color:var(--chrome-lo);
}
.map-key i{
  display:inline-block;width:9px;height:9px;border-radius:50%;
  margin-right:7px;vertical-align:middle;
}
.map-key .k-hub{background:var(--jmp-area-accent)}
.map-key .k-pin{background:var(--jmp-area-pin)}

/* ---------- Town ticker ---------- */
.ticker{
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  background:var(--carbon-2);
  overflow:hidden;white-space:nowrap;padding:14px 0;
}
.ticker-track{
  display:inline-flex;gap:56px;padding-left:56px;
  animation:jmp-ticker var(--jmp-ticker-time) linear infinite;
}
.ticker:hover .ticker-track{animation-play-state:paused}
.ticker span{
  font-family:var(--display);font-size:.66rem;letter-spacing:.28em;
  text-transform:uppercase;color:var(--chrome-lo);
  display:inline-flex;align-items:center;gap:56px;
}
.ticker span::after{
  content:"";width:6px;height:6px;background:var(--blue);transform:rotate(45deg);
}
/* Exactly half the track, because the list is emitted twice — so the loop
   restarts on an identical frame and there is no visible jump. */
@keyframes jmp-ticker{to{transform:translateX(-50%)}}

/* =====================================================
   TESTIMONIALS
   Editable: --jmp-quote-bg --jmp-quote-card
   --jmp-quote-accent --jmp-quote-cols
   ===================================================== */
[data-jmp-section="testimonials"]{
  --jmp-space-top:110px;
  --jmp-space-bottom:110px;
  --jmp-quote-bg:#0e1218;
  --jmp-quote-card:#161b24;
  --jmp-quote-accent:#5aa8ff;
  --jmp-quote-cols:3;
}
.site-main > .testimonials{background:var(--jmp-quote-bg)}
.quotes{display:grid;grid-template-columns:repeat(var(--jmp-quote-cols),1fr);gap:18px}
.quote{
  background:var(--jmp-quote-card);border:1px solid var(--line);
  padding:34px 30px;position:relative;margin:0;
  display:flex;flex-direction:column;
}
/* Oversized opening quote mark, set behind the text. */
.quote::before{
  content:"\201C";position:absolute;top:10px;right:22px;
  font-family:Georgia,serif;font-size:5rem;line-height:1;
  color:var(--jmp-quote-accent);opacity:.28;pointer-events:none;
}
.quote .stars{
  color:var(--jmp-quote-accent);letter-spacing:.2em;
  font-size:.8rem;margin-bottom:16px;
}
.quote blockquote{
  color:var(--chrome);font-size:.98rem;margin:0 0 22px;
  position:relative;z-index:1;
}
.quote figcaption{margin-top:auto}
.quote figcaption b{display:block;color:var(--chrome-hi);font-weight:700}
.quote figcaption span{
  font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;color:var(--chrome-lo);
}

/* =====================================================
   BRANDS STRIP
   Editable: --jmp-brands-text --jmp-brands-hover
   ===================================================== */
[data-jmp-section="brands"]{
  --jmp-space-top:50px;
  --jmp-space-bottom:50px;
  --jmp-brands-text:#8a9099;
  --jmp-brands-hover:#f2f4f7;
  display:block;
  padding-block:var(--jmp-space-top,50px) var(--jmp-space-bottom,50px);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
}
.brands .row{
  display:flex;flex-wrap:wrap;justify-content:space-between;
  gap:22px 40px;align-items:center;
}
.brands .row span{
  font-family:var(--display);font-size:.72rem;letter-spacing:.18em;
  text-transform:uppercase;color:var(--jmp-brands-text);
  opacity:.85;transition:color .2s,opacity .2s;
}
.brands .row span:hover{color:var(--jmp-brands-hover);opacity:1}
.brands .note{text-align:center;margin-top:26px;font-size:.8rem;letter-spacing:.06em}

/* =====================================================
   DISCOUNT BAND
   Editable: --jmp-perks-bg --jmp-perks-accent
   ===================================================== */
[data-jmp-section="perks"]{
  --jmp-space-top:36px;
  --jmp-space-bottom:36px;
  --jmp-perks-bg:#0e1218;
  --jmp-perks-accent:#5aa8ff;
  display:block;
  background:var(--jmp-perks-bg);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding-block:var(--jmp-space-top,36px) var(--jmp-space-bottom,36px);
}
.perks .container{
  display:flex;align-items:center;gap:40px;
  flex-wrap:wrap;justify-content:space-between;
}
.perks .lede{display:flex;align-items:center;gap:20px}
.perks .shield{
  width:52px;height:52px;flex:none;display:grid;place-items:center;
  border:1px solid rgba(201,206,214,.25);
  transform:skewX(var(--skew));
  background:linear-gradient(180deg,rgba(255,255,255,.06),transparent);
}
.perks .shield svg{
  width:24px;height:24px;
  stroke:var(--jmp-perks-accent);fill:none;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
  transform:skewX(calc(var(--skew) * -1));
}
.perks h3{
  font-family:var(--body);font-style:italic;font-weight:900;
  font-size:1.4rem;text-transform:uppercase;margin-bottom:4px;
}
.perks p{font-size:.92rem;max-width:44ch}
.perks .tags{display:flex;gap:10px;flex-wrap:wrap}
.perks .tags span{
  font-family:var(--display);font-size:.58rem;letter-spacing:.2em;
  text-transform:uppercase;color:var(--chrome);
  padding:11px 16px;border:1px solid rgba(201,206,214,.25);
  transform:skewX(var(--skew));
}
.perks .tags span i{font-style:normal;display:inline-block;transform:skewX(calc(var(--skew) * -1))}

/* =====================================================
   FAQ
   ===================================================== */
[data-jmp-section="faq"]{
  --jmp-space-top:110px;
  --jmp-space-bottom:110px;--jmp-faq-accent:#5aa8ff}
.faq .container{display:grid;grid-template-columns:.7fr 1.3fr;gap:70px;align-items:start}
.faq details{border-bottom:1px solid var(--line)}
.faq summary{
  list-style:none;cursor:pointer;padding:22px 0;
  display:flex;justify-content:space-between;align-items:center;gap:20px;
  font-weight:600;color:var(--chrome-hi);font-size:1.05rem;
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
  content:"+";font-family:var(--display);color:var(--jmp-faq-accent);
  font-size:1.1rem;flex:none;transition:transform .25s;
}
.faq details[open] summary::after{transform:rotate(45deg)}
.faq details p{padding:0 0 22px;max-width:70ch}

/* =====================================================
   REQUEST FORM
   Editable: --jmp-form-bg --jmp-form-accent
   ===================================================== */
[data-jmp-section="form"]{
  --jmp-space-top:110px;
  --jmp-space-bottom:110px;
  --jmp-form-bg:#161b24;
  --jmp-form-accent:#1f7be6;
}
.site-main > .quote-section{
  background:
    radial-gradient(ellipse 60% 60% at 85% 20%,
      color-mix(in srgb,var(--jmp-form-accent) 20%,transparent),transparent 60%),
    var(--carbon);
}
@supports not (background:color-mix(in srgb,#000 50%,transparent)){
  .site-main > .quote-section{background:var(--carbon)}
}
.quote-section .container{display:grid;grid-template-columns:.85fr 1.15fr;gap:70px;align-items:start}
.quote-lead{margin-top:18px;font-size:1.05rem}

.contact-list{list-style:none;display:grid;gap:22px;margin-top:36px}
.contact-list li{display:grid;grid-template-columns:auto 1fr;gap:18px;align-items:center}
.contact-list .ic{
  width:48px;height:48px;display:grid;place-items:center;
  border:1px solid rgba(201,206,214,.25);transform:skewX(var(--skew));
}
.contact-list .ic svg{
  width:20px;height:20px;stroke:var(--blue-hi);fill:none;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;
  transform:skewX(calc(var(--skew) * -1));
}
.contact-list b{display:block;color:var(--chrome-hi);font-weight:700}
.contact-list span{font-size:.9rem;color:var(--chrome-lo)}

.form{background:var(--jmp-form-bg);border:1px solid var(--line);padding:40px;position:relative}
.form::before{
  content:"";position:absolute;inset:0 0 auto 0;height:3px;
  background:linear-gradient(90deg,var(--blue-lo),var(--jmp-form-accent),var(--chrome));
}
/* Honeypot: removed from sight and from the accessibility tree, but still a
   real focusable input for anything filling in every field it can find. */
.jmp-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.seg{display:grid;grid-template-columns:1fr 1fr;border:1px solid var(--line);margin-bottom:26px}
.seg label{
  text-align:center;padding:14px;cursor:pointer;
  font-family:var(--display);font-size:.62rem;letter-spacing:.22em;
  text-transform:uppercase;color:var(--chrome-lo);
  transition:background .2s,color .2s;
}
.seg input{position:absolute;opacity:0;pointer-events:none}
.seg input:checked + label{
  background:linear-gradient(180deg,var(--blue-hi),var(--jmp-form-accent) 45%,var(--blue-lo));
  color:#fff;
}
.seg input:focus-visible + label{outline:2px solid var(--blue-hi);outline-offset:-2px}

.fields{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.field{display:flex;flex-direction:column;gap:7px;min-width:0}
.field.full{grid-column:1/-1}
.field[hidden]{display:none}
.field > label{
  font-family:var(--display);font-size:.58rem;letter-spacing:.2em;
  text-transform:uppercase;color:var(--chrome-lo);
}
.field .req{color:var(--jmp-form-accent);margin-left:4px}
.field input,.field select,.field textarea{
  background:var(--carbon);border:1px solid rgba(201,206,214,.18);
  color:var(--chrome-hi);padding:14px 16px;
  font:inherit;font-size:.95rem;border-radius:2px;
  transition:border-color .2s,box-shadow .2s;
}
.field select{
  appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--chrome) 50%),
    linear-gradient(135deg,var(--chrome) 50%,transparent 50%);
  background-position:calc(100% - 20px) 50%,calc(100% - 15px) 50%;
  background-size:5px 5px;background-repeat:no-repeat;
}
.field textarea{min-height:120px;resize:vertical}
.field input:focus,.field select:focus,.field textarea:focus{
  outline:0;border-color:var(--jmp-form-accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--jmp-form-accent) 20%,transparent);
}
@supports not (box-shadow:0 0 0 3px color-mix(in srgb,#000 50%,transparent)){
  .field input:focus,.field select:focus,.field textarea:focus{box-shadow:0 0 0 3px rgba(31,123,230,.2)}
}
.field .tick{display:flex;align-items:center;gap:10px;color:var(--chrome);font-size:.95rem}
.field .tick input{width:auto;padding:0}

/* Validation state. The message is always in the DOM so a screen reader with
   role=alert announces it the moment it is filled. */
.field-error{font-size:.78rem;color:var(--red);min-height:0}
.field.has-error input,.field.has-error select,.field.has-error textarea{border-color:var(--red)}
.field.has-error .field-error{min-height:1.2em}
.form-error{color:var(--red);font-size:.88rem;margin-top:16px;min-height:0}

.form-foot{
  display:flex;align-items:center;justify-content:space-between;
  gap:20px;margin-top:26px;flex-wrap:wrap;
}
.form-foot small{color:var(--chrome-lo);font-size:.8rem;max-width:34ch}
.btn.is-sending{opacity:.6;pointer-events:none}

.success{display:none;text-align:center;padding:50px 20px}
.success.show{display:block;animation:jmp-fade .4s ease}
.success .check{
  width:72px;height:72px;margin:0 auto 22px;display:grid;place-items:center;
  background:linear-gradient(180deg,var(--blue-hi),var(--blue-lo));
  transform:skewX(var(--skew));
}
.success .check svg{
  width:32px;height:32px;stroke:#fff;fill:none;stroke-width:3;
  stroke-linecap:round;stroke-linejoin:round;
  transform:skewX(calc(var(--skew) * -1));
}
.success h3{
  font-family:var(--body);font-style:italic;font-size:1.6rem;
  text-transform:uppercase;margin-bottom:10px;
}

/* =====================================================
   CLOSING BAND
   Editable: --jmp-cta-from --jmp-cta-to
   ===================================================== */
[data-jmp-section="cta"]{
  --jmp-space-top:80px;
  --jmp-space-bottom:80px;
  --jmp-cta-from:#0b4fb5;
  --jmp-cta-to:#5aa8ff;
  display:block;
  padding-block:var(--jmp-space-top,80px) var(--jmp-space-bottom,80px);position:relative;overflow:hidden;
  background:linear-gradient(115deg,var(--jmp-cta-from) 0%,
    color-mix(in srgb,var(--jmp-cta-from) 50%,var(--jmp-cta-to)) 45%,
    var(--jmp-cta-to) 100%);
}
@supports not (background:color-mix(in srgb,#000 50%,transparent)){
  [data-jmp-section="cta"]{background:linear-gradient(115deg,var(--jmp-cta-from),var(--jmp-cta-to))}
}
.cta-band::before{
  content:"";position:absolute;inset:0;
  background:linear-gradient(115deg,transparent 45%,rgba(255,255,255,.18) 50%,transparent 55%);
  transform:skewX(-12deg);pointer-events:none;
}
.cta-band .container{
  position:relative;display:flex;justify-content:space-between;
  align-items:center;gap:30px;flex-wrap:wrap;
}
.cta-band h2{color:#fff;font-size:clamp(26px,3.4vw,42px)}
.cta-band p{color:rgba(255,255,255,.82);margin-top:8px}
.cta-band .btn-chrome{box-shadow:0 20px 40px -15px rgba(0,0,0,.5)}

/* =====================================================
   HEARTBEAT DIVIDER
   Editable: --jmp-pulse-color --jmp-pulse-weight
   --jmp-pulse-glow --jmp-pulse-speed
   ===================================================== */
[data-jmp-section="pulse"]{
  --jmp-pulse-amp:100%;
  --jmp-space-top:0px;
  --jmp-space-bottom:0px;
  --jmp-pulse-color:#1f7be6;
  --jmp-pulse-weight:2px;
  --jmp-pulse-glow:6px;
  --jmp-pulse-speed:1600ms;
  display:block;
}
/* Rail across the full width with the beat fixed at dead centre. Nothing moves:
   a mask edge travels left to right, first revealing the trace and then erasing
   it, so the whole thing including the beat clears before it redraws.

   The earlier version slid a centred beat with two flat lines wiping outward,
   and the repeat re-drew only the beat — so the straight runs never cleared. */
.pulse{
  display:block;position:relative;
  width:100%;
  height:clamp(30px,2.6vw,52px);
}
.pulse-trace{
  position:absolute;inset:0;
  /* The two rails flex to fill whatever the beat does not take, which centres
     it without justify-content — and keeps them from overlapping it. */
  display:flex;align-items:center;
  /* Complete by default, so a browser that cannot animate a custom percentage
     shows the trace rather than nothing. The animations override this. */
  --jmp-wipe:100%;
  --jmp-erase:0%;
  /* The mask is a hard edge swept by --jmp-wipe. Two stops rather than a
     gradient ramp so the trace has a crisp leading edge like a monitor. */
  -webkit-mask-image:linear-gradient(90deg,#000 0 var(--jmp-wipe,0%),transparent var(--jmp-wipe,0%));
  mask-image:linear-gradient(90deg,#000 0 var(--jmp-wipe,0%),transparent var(--jmp-wipe,0%));
}
/* Flex children either side of the beat, not one line behind it. The beat is a
   stroked path with no fill, so a full-width rail showed straight through the
   zigzag. These simply stop where it begins. */
.pulse-rail{
  flex:1;
  height:var(--jmp-pulse-weight);
  background:var(--jmp-pulse-color);
  border-radius:999px;
  box-shadow:0 0 var(--jmp-pulse-glow)
    color-mix(in srgb,var(--jmp-pulse-color) 55%,transparent);
  /* The beat's own baseline sits fractionally off the flex centre line at some
     sizes; aligning to it rather than to the container keeps the join clean. */
  align-self:center;
}
.pulse-beat{
  position:relative;
  height:100%;width:auto;overflow:visible;
  /* Amplitude scales the beat vertically only, so the spikes grow taller
     without the trace getting wider. Divided by 100% to turn the percentage
     setting into the plain number scaleY() requires. */
  transform:scaleY(calc(var(--jmp-pulse-amp,100%) / 100%));
}
/* Scaling an SVG scales its strokes too, so the line would thicken as the beat
   grew. non-scaling-stroke keeps the width at whatever the thickness setting
   says, independent of the transform. */
.pulse-beat path{vector-effect:non-scaling-stroke}
.pulse-beat path{
  fill:none;
  stroke:var(--jmp-pulse-color);
  stroke-width:var(--jmp-pulse-weight);
  stroke-linecap:round;stroke-linejoin:round;
  filter:drop-shadow(0 0 var(--jmp-pulse-glow)
    color-mix(in srgb,var(--jmp-pulse-color) 55%,transparent));
}
@supports not (filter:drop-shadow(0 0 1px color-mix(in srgb,#000 50%,transparent))){
  .pulse-beat path{filter:drop-shadow(0 0 var(--jmp-pulse-glow) var(--blue-glow))}
  .pulse-rail{box-shadow:0 0 var(--jmp-pulse-glow) var(--blue-glow)}
}

/* Custom property so the mask position can be animated — a raw gradient stop
   cannot be, but a registered percentage can. */
@property --jmp-wipe{
  syntax:'<percentage>';
  inherits:false;
  initial-value:0%;
}

/* Draw once: the trace sweeps in from the left and stays. */
.pulse.in-view .pulse-trace{
  animation:jmp-pulse-in var(--jmp-pulse-speed) cubic-bezier(.3,0,.2,1) forwards;
}
@keyframes jmp-pulse-in{from{--jmp-wipe:0%}to{--jmp-wipe:100%}}

/* Sweep: draw in, hold, then the same edge carries on and erases it. */
.pulse-wrap.is-repeating .pulse.in-view .pulse-trace{
  animation:jmp-pulse-sweep calc(var(--jmp-pulse-speed) * 3) linear infinite;
  -webkit-mask-image:linear-gradient(90deg,
    transparent 0 var(--jmp-erase,0%),
    #000 var(--jmp-erase,0%) var(--jmp-wipe,0%),
    transparent var(--jmp-wipe,0%));
  mask-image:linear-gradient(90deg,
    transparent 0 var(--jmp-erase,0%),
    #000 var(--jmp-erase,0%) var(--jmp-wipe,0%),
    transparent var(--jmp-wipe,0%));
}
@property --jmp-erase{
  syntax:'<percentage>';
  inherits:false;
  initial-value:0%;
}
@keyframes jmp-pulse-sweep{
  /* Leading edge crosses, drawing the trace. */
  0%{--jmp-wipe:0%;--jmp-erase:0%}
  40%{--jmp-wipe:100%;--jmp-erase:0%}
  /* Complete, held. */
  55%{--jmp-wipe:100%;--jmp-erase:0%}
  /* Trailing edge follows, erasing everything in front of it. */
  95%{--jmp-wipe:100%;--jmp-erase:100%}
  100%{--jmp-wipe:100%;--jmp-erase:100%}
}

/* No feature test needed for the fallback: --jmp-wipe defaults to 100% on the
   element, so a browser that cannot animate the percentage renders the trace
   complete rather than invisible. @property only overrides that default while
   an animation is actually running. */

/* A travelling line is decoration; a complete static one is the fallback. */
@media (prefers-reduced-motion:reduce){
  .pulse-trace{--jmp-wipe:100%;--jmp-erase:0%}
  .pulse.in-view .pulse-trace,
  .pulse-wrap.is-repeating .pulse.in-view .pulse-trace{animation:none}
}

/* ---------- Hero background patterns ----------
   Each sets the three --pat-* properties the ::before layer reads. Stroke
   colours stay low-contrast so the pattern never competes with the copy. */
.hero.is-pattern-grid{
  --pat-img:
    linear-gradient(rgba(201,206,214,.05) 1px,transparent 1px),
    linear-gradient(90deg,rgba(201,206,214,.05) 1px,transparent 1px);
  --pat-size:48px 48px;
}
.hero.is-pattern-honeycomb{
  --pat-img:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='56' height='97' viewBox='0 0 56 97'%3E%3Cpath d='M28 0L56 16v32L28 64 0 48V16z' fill='none' stroke='rgba(201,206,214,0.07)' stroke-width='1.1'/%3E%3Cpath d='M28 64l28 16v32L28 128 0 112V80z' fill='none' stroke='rgba(201,206,214,0.07)' stroke-width='1.1'/%3E%3C/svg%3E");
  --pat-size:56px 97px;
}
.hero.is-pattern-diagonal{
  --pat-img:repeating-linear-gradient(115deg,
    rgba(201,206,214,.06) 0 1px,transparent 1px 14px);
  --pat-size:auto;
}
.hero.is-pattern-carbon{
  --pat-img:
    repeating-linear-gradient(45deg,rgba(201,206,214,.05) 0 1px,transparent 1px 6px),
    repeating-linear-gradient(-45deg,rgba(201,206,214,.05) 0 1px,transparent 1px 6px);
  --pat-size:auto;
}
.hero.is-pattern-dots{
  --pat-img:radial-gradient(rgba(201,206,214,.11) 1.2px,transparent 1.2px);
  --pat-size:26px 26px;
}
.hero.is-pattern-plate{
  --pat-img:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='64' viewBox='0 0 64 64'%3E%3Cg fill='none' stroke='rgba(201,206,214,0.08)' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M8 20l14-9'/%3E%3Cpath d='M42 52l14-9'/%3E%3Cpath d='M42 11l14 9'/%3E%3Cpath d='M8 43l14 9'/%3E%3C/g%3E%3C/svg%3E");
  --pat-size:64px 64px;
}
.hero.is-pattern-none{--pat-img:none;--pat-size:auto}

/* =====================================================
   FOOTER
   Editable: --jmp-foot-bg --jmp-foot-head --jmp-foot-link
   --jmp-foot-link-hi --jmp-foot-logo-h
   ===================================================== */
[data-jmp-section="footer"]{
  --jmp-foot-bg:#0e1218;
  --jmp-foot-head:#5aa8ff;
  --jmp-foot-link:#8a9099;
  --jmp-foot-link-hi:#f2f4f7;
  --jmp-foot-logo-h:100px;
  display:block;
  background:var(--jmp-foot-bg);
  border-top:1px solid var(--line);
  padding:70px 0 30px;
}
/* Flex rather than a fixed four-track grid: a column can be emptied and removed,
   and four declared tracks would leave a hole where it used to be. The brand
   block takes a wider basis; the link columns share what is left evenly. */
.foot-grid{display:flex;flex-wrap:wrap;gap:40px;margin-bottom:50px}
.foot-grid > *{flex:1 1 150px;min-width:0}
.foot-grid > :first-child{flex:1.6 1 260px}
.foot-grid h4{
  font-family:var(--display);font-size:.62rem;letter-spacing:.24em;text-transform:uppercase;
  color:var(--jmp-foot-head);margin-bottom:18px;
}
.foot-grid ul{list-style:none;display:grid;gap:10px}
.foot-grid a{font-size:.92rem;color:var(--jmp-foot-link);transition:color .2s}
.foot-grid a:hover{color:var(--jmp-foot-link-hi)}
.foot-grid img{
  height:var(--jmp-foot-logo-h);width:auto;
  max-width:100%;object-fit:contain;object-position:left center;
  margin-bottom:12px;
}
.foot-grid p{font-size:.9rem;max-width:36ch}
.foot-bottom{
  border-top:1px solid var(--line);padding-top:26px;
  display:flex;justify-content:space-between;flex-wrap:wrap;gap:12px;
  font-size:.78rem;letter-spacing:.06em;color:var(--chrome-lo);
}
.foot-bottom a{color:var(--chrome-lo)}
.foot-bottom a:hover{color:var(--chrome-hi)}

/* The header takes focus when back-to-top is used, so the keyboard lands where
   the page went. It is a scroll target, not an interactive control, so it does
   not show a focus ring. */
.site-header:focus{outline:none}

/* ---------- Back to top ----------
   Hidden until the visitor has scrolled a screen, so it never covers content
   on a page they have only just opened. */
.back-top{
  /* Below the pinned nav (50) so it can never float over it on a short
     viewport, above ordinary content. */
  position:fixed;right:22px;bottom:22px;z-index:45;
  width:46px;height:46px;display:grid;place-items:center;
  background:linear-gradient(180deg,var(--blue-hi),var(--blue) 45%,var(--blue-lo));
  color:#fff;border-radius:5px;
  box-shadow:0 10px 28px rgba(0,0,0,.5);
  opacity:0;visibility:hidden;transform:translateY(10px);
  transition:opacity .25s,transform .25s,visibility .25s;
}
.back-top.is-visible{opacity:1;visibility:visible;transform:none}
.back-top:hover{filter:brightness(1.1)}
.back-top svg{
  width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
}
/* Clears the sticky call bar on phones, which occupies the same corner. */
@media (max-width:640px){
  .back-top{bottom:72px;right:16px;width:42px;height:42px}
}

/* Mobile sticky call bar */
.mobile-call{
  display:none;position:fixed;bottom:0;left:0;right:0;z-index:60;
  background:linear-gradient(180deg,var(--blue-hi),var(--blue) 45%,var(--blue-lo));
  color:#fff;text-align:center;padding:16px;
  font-family:var(--display);font-size:.7rem;letter-spacing:.22em;text-transform:uppercase;
  box-shadow:0 -10px 30px rgba(0,0,0,.6);
}

/* ---------- Responsive ---------- */
@media (max-width:1024px){
  /* Brand block takes a full row, link columns share the next. */
  .foot-grid > :first-child{flex:1 1 100%}
  /* Logo leads on narrow screens — it is the strongest brand cue and the
     copy reads better beneath it than beside it. */
  .hero .container{grid-template-columns:1fr;gap:40px}
  .hero-logo{order:-1}
  .hero-logo img{max-width:340px}
  /* Cap at two columns regardless of the chosen setting — three 28px-padded
     cards below 1024px leaves each too narrow to read. */
  .services .grid{grid-template-columns:repeat(min(var(--jmp-svc-cols),2),1fr)}
  /* Argument above, tiers below — side by side they get too narrow to read. */
  .pm .container{grid-template-columns:1fr;gap:46px}
  /* Two per row; the connecting rule only reads across a single row, so it
     goes rather than jumping between wrapped lines. */
  .steps{grid-template-columns:repeat(2,1fr);gap:40px 20px}
  .steps::before{display:none}
  /* Map below the list — side by side the pins get too tight to label. */
  .area .container{grid-template-columns:1fr;gap:46px}
  /* Two per row max: a quote needs a readable measure more than it needs to
     sit beside its neighbours. */
  .quotes{grid-template-columns:repeat(min(var(--jmp-quote-cols),2),1fr)}
  /* Brand names distribute rather than justify, so a short last row does not
     stretch into gappy spacing. */
  .brands .row{justify-content:flex-start;gap:18px 32px}
  .faq .container,.quote-section .container{grid-template-columns:1fr;gap:46px}
}
@media (max-width:820px){
  /* Stack the two panels; the skewed seam only reads as a divider side by
     side, so it gives way to a plain border between the stacked halves. */
  .split{grid-template-columns:1fr}
  /* Stacked, there is no midpoint for the seam or the angled wash boundary to
     follow — a plain border divides the halves instead. The washes move back
     onto the panels themselves, where a horizontal split is the correct shape
     and the alignment problem cannot arise. */
  .split::before,.split::after{display:none}
  .path{padding:40px 28px;border-top:1px solid var(--line)}
  .path:first-child{border-top:0}
  .path.residential{background:linear-gradient(160deg,rgba(201,206,214,.07),transparent 60%)}
  .path.commercial{
    background:linear-gradient(160deg,
      color-mix(in srgb,var(--jmp-paths-com) 10%,transparent),transparent 60%);
  }
  .nav-links,.nav-phone{display:none}
  .brand img{height:calc(var(--jmp-logo-h) * .8)}
  .burger{display:flex}
  .topbar .container span:nth-child(2){display:none}
  .topbar .container{height:auto;padding-block:8px}
  .topbar .container span:nth-child(1){white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .topbar .container span:nth-child(3){white-space:nowrap}
}
@media (max-width:640px){
  body{font-size:16px;padding-bottom:56px}
  .brand-word,.jmp-logo-fallback{font-size:1.08rem}
  /* Mobile keeps a flat 80px regardless of the desktop setting — the spacing
     controls exist to fix desktop, and the phone layout already reads well. */
  .site-main > section{padding-block:80px}
  .foot-grid > *{flex:1 1 100%}
  .mobile-call{display:block}
  .nav .btn{display:none}
  .site-main > .hero{min-height:auto}
  .hero-stats{gap:20px}
  .services .grid{grid-template-columns:1fr}
  .steps{grid-template-columns:1fr}
  .step{padding-right:0}
  .towns{grid-template-columns:1fr}
  .route{padding:18px}
  .quotes{grid-template-columns:1fr}
  /* The band stacks: heading block, then the qualifying groups beneath. */
  .perks .container{flex-direction:column;align-items:flex-start;gap:24px}
  .fields{grid-template-columns:1fr}
  .field.full{grid-column:auto}
  .form{padding:26px 20px}
  /* Full-width tabs so both are comfortable tap targets. */
  .tabs{width:100%}
  .tab{flex:1;padding:14px 10px;text-align:center}
  /* The vertical frequency label costs width the card cannot spare on a phone,
     so it lies flat above the text with a rule underneath instead. */
  .plan{grid-template-columns:1fr;gap:14px;padding:24px}
  .plan .freq{
    writing-mode:horizontal-tb;transform:none;
    height:auto;padding:0 0 10px;
    border-right:0;border-bottom:2px solid var(--jmp-pm-accent);
    justify-self:start;
  }
}
