/* --- tokens/colors.css --- */
:root{
/* Ink — the dark ground the brand lives on */
--gp-ink-900:#07090B;--gp-ink-850:#0C1013;--gp-ink-800:#12171B;--gp-ink-700:#1A2126;--gp-ink-600:#232C33;--gp-ink-500:#33404A;
/* Steel & chrome — the metal in the shield */
--gp-steel-400:#707F8A;--gp-steel-300:#8494A0;--gp-chrome-200:#B6C3CC;--gp-chrome-100:#DCE3E8;--gp-chrome-050:#F1F4F6;--gp-white:#FFFFFF;
/* Precision blue — the underline in the lockup */
--gp-blue-700:#0B4E86;--gp-blue-600:#0F62A8;--gp-blue-550:#1574B4;--gp-blue-500:#1B7FC4;--gp-blue-400:#3D9BE0;--gp-blue-050:#E6F1FA;
/* Flag — brand accent only, never a status.
   --gp-flag-graphite stands in for the black band wherever the tricolour sits
   on the ink ground: #0A0A0A on #07090B is invisible, so the rule read as if
   it started a third of the way in. */
--gp-flag-black:#0A0A0A;--gp-flag-graphite:#2E3338;--gp-flag-red:#C8102E;--gp-flag-gold:#F2B705;
/* Semantic status */
--gp-green-500:#2E9E5B;--gp-amber-500:#D98A0B;--gp-red-500:#D92D3C;

/* Surfaces */
--gp-surface-page:var(--gp-ink-900);--gp-surface-card:var(--gp-ink-800);--gp-surface-raised:var(--gp-ink-700);--gp-surface-sunken:var(--gp-ink-850);--gp-surface-inverse:var(--gp-chrome-050);
/* Text */
--gp-text-strong:var(--gp-white);--gp-text-body:var(--gp-chrome-100);--gp-text-muted:var(--gp-steel-300);--gp-text-faint:var(--gp-steel-400);--gp-text-accent:var(--gp-blue-400);--gp-text-on-accent:var(--gp-white);--gp-text-on-inverse:var(--gp-ink-900);
/* Lines */
--gp-border-hairline:rgba(182,195,204,.14);--gp-border-subtle:rgba(182,195,204,.22);--gp-border-strong:rgba(182,195,204,.38);--gp-border-accent:var(--gp-blue-500);
/* Interactive.
   The action fill is blue-600, not the logo blue-500: white on #1B7FC4 measures
   4.30:1 and button labels are 13px, so every primary CTA on the site was
   failing WCAG AA. blue-500/400 stay in play for accent text and indicators,
   where they sit on the ink ground and read at 4.6-6.6:1. */
--gp-action-primary:var(--gp-blue-600);--gp-action-primary-hover:var(--gp-blue-550);--gp-action-primary-press:var(--gp-blue-700);--gp-focus-ring:rgba(61,155,224,.55);
/* Textures */
--gp-gradient-chrome:linear-gradient(177deg,#FFFFFF 0%,#D5DEE4 30%,#8A98A3 50%,#F0F4F7 66%,#A7B4BE 88%,#E4EAEE 100%); /* @kind color */
--gp-gradient-flag:linear-gradient(90deg,var(--gp-flag-graphite) 0 33.334%,var(--gp-flag-red) 33.334% 66.667%,var(--gp-flag-gold) 66.667% 100%); /* @kind color */
--gp-gradient-protect:linear-gradient(180deg,rgba(7,9,11,0) 0%,rgba(7,9,11,.72) 58%,var(--gp-ink-900) 100%); /* @kind color */
--gp-gradient-hairline:linear-gradient(90deg,rgba(182,195,204,0) 0%,rgba(182,195,204,.45) 50%,rgba(182,195,204,0) 100%); /* @kind color */
}

/* --- tokens/fonts.css --- */
/* Self-hosted, latin subset only, pulled by tools/fetch-fonts.mjs.
   These were a render-blocking stylesheet on fonts.googleapis.com: measured
   348ms of blocked first paint on the home page, plus the whole of the home
   page's 0.0775 CLS, which was the hero jumping up 37px at 4.3s when the real
   face replaced the fallback. All three families are SIL OFL 1.1.
   Saira Condensed ships no italic, which is why the display face is a
   synthetic oblique - see the background-clip:text note in tokens/base.css. */
@font-face{font-family:'Barlow';font-style:italic;font-weight:400;font-display:swap;src:url(/assets/fonts/barlow-400-italic.woff2) format('woff2');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:'Barlow';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/barlow-400.woff2) format('woff2');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:'Barlow';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/barlow-500.woff2) format('woff2');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:'Barlow';font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/barlow-600.woff2) format('woff2');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:'Barlow';font-style:normal;font-weight:700;font-display:swap;src:url(/assets/fonts/barlow-700.woff2) format('woff2');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:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/ibm-plex-mono-400.woff2) format('woff2');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:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/ibm-plex-mono-500.woff2) format('woff2');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:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/ibm-plex-mono-600.woff2) format('woff2');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:'Saira Condensed';font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/saira-condensed-600.woff2) format('woff2');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:'Saira Condensed';font-style:normal;font-weight:700;font-display:swap;src:url(/assets/fonts/saira-condensed-700.woff2) format('woff2');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:'Saira Condensed';font-style:normal;font-weight:800;font-display:swap;src:url(/assets/fonts/saira-condensed-800.woff2) format('woff2');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}

/* SUBSTITUTION: no brand font files were supplied. The logo wordmark is a heavy
   italic techno sans; Saira Condensed is the nearest free match. Barlow carries body
   copy, IBM Plex Mono carries specs/part numbers. Swap in the real files when available. */

:root{--gp-font-display:"Saira Condensed","Arial Narrow",sans-serif;--gp-font-body:"Barlow",system-ui,sans-serif;--gp-font-mono:"IBM Plex Mono",ui-monospace,monospace}

/* --- tokens/typography.css --- */
:root{
/* Display — Saira Condensed, italic, uppercase. Logo voice. */
--gp-display-1:800 italic 76px/.92 var(--gp-font-display);
--gp-display-2:800 italic 56px/.96 var(--gp-font-display);
--gp-display-3:800 italic 40px/1 var(--gp-font-display);
/* Headings — condensed caps, upright */
--gp-heading-1:700 32px/1.1 var(--gp-font-display);
--gp-heading-2:700 24px/1.15 var(--gp-font-display);
--gp-heading-3:600 19px/1.25 var(--gp-font-display);
/* Body — Barlow */
--gp-body-lg:400 18px/1.6 var(--gp-font-body);
--gp-body-md:400 16px/1.6 var(--gp-font-body);
--gp-body-sm:400 14px/1.55 var(--gp-font-body);
--gp-body-xs:400 13px/1.5 var(--gp-font-body);
--gp-label:600 13px/1.2 var(--gp-font-body);
/* Eyebrow / overline — the "MOBILE SERVICE" tier of the lockup */
--gp-eyebrow:700 12px/1.2 var(--gp-font-body);
--gp-eyebrow-tracking:.22em;
/* Mono — specs, part numbers, torque, VIN */
--gp-mono-md:500 14px/1.5 var(--gp-font-mono);
--gp-mono-sm:500 12px/1.45 var(--gp-font-mono);
/* Tracking */
--gp-track-display:-.01em;--gp-track-caps:.04em;--gp-track-tight:-.015em;
}

/* --- tokens/spacing.css --- */
:root{
--gp-space-0:0px;--gp-space-1:4px;--gp-space-2:8px;--gp-space-3:12px;--gp-space-4:16px;--gp-space-5:20px;--gp-space-6:24px;--gp-space-8:32px;--gp-space-10:40px;--gp-space-12:48px;--gp-space-16:64px;--gp-space-20:80px;--gp-space-24:96px;--gp-space-32:128px;
--gp-gutter:24px;--gp-container:1200px;--gp-container-narrow:760px;
--gp-section-y:96px;--gp-section-y-sm:56px;
--gp-control-h-sm:34px;--gp-control-h-md:42px;--gp-control-h-lg:52px;
--gp-tap-min:44px;
}

/* --- tokens/radius.css --- */
:root{
/* Engineered, not soft. Nothing rounder than 10px except pills. */
--gp-radius-0:0px;--gp-radius-1:2px;--gp-radius-2:4px;--gp-radius-3:6px;--gp-radius-4:10px;--gp-radius-pill:999px;
--gp-radius-control:var(--gp-radius-2);--gp-radius-card:var(--gp-radius-3);--gp-radius-modal:var(--gp-radius-4);
}

/* --- tokens/elevation.css --- */
:root{
--gp-shadow-0:none;
--gp-shadow-1:0 1px 2px rgba(0,0,0,.5);
--gp-shadow-2:0 4px 14px rgba(0,0,0,.45);
--gp-shadow-3:0 14px 40px rgba(0,0,0,.55);
--gp-shadow-4:0 28px 80px rgba(0,0,0,.65);
/* Chrome bevel — top highlight + bottom shade, how metal reads on dark */
--gp-bevel-up:inset 0 1px 0 rgba(255,255,255,.14),inset 0 -1px 0 rgba(0,0,0,.5); /* @kind shadow */
--gp-bevel-down:inset 0 1px 3px rgba(0,0,0,.6); /* @kind shadow */
--gp-glow-accent:0 0 0 1px rgba(27,127,196,.5),0 6px 24px rgba(27,127,196,.28);
}

/* --- tokens/motion.css --- */
:root{
/* Mechanical, not playful. No bounce, no overshoot, ever. */
--gp-ease-standard:cubic-bezier(.2,0,.2,1);/* @kind other */
--gp-ease-out:cubic-bezier(.16,1,.3,1);/* @kind other */
--gp-ease-in:cubic-bezier(.4,0,1,1);/* @kind other */
--gp-dur-instant:90ms; /* @kind other */
--gp-dur-fast:140ms; /* @kind other */
--gp-dur-base:220ms; /* @kind other */
--gp-dur-slow:380ms; /* @kind other */
--gp-transition-control:background-color var(--gp-dur-fast) var(--gp-ease-standard),border-color var(--gp-dur-fast) var(--gp-ease-standard),color var(--gp-dur-fast) var(--gp-ease-standard),box-shadow var(--gp-dur-fast) var(--gp-ease-standard); /* @kind other */
}

/* --- tokens/base.css --- */
*,*::before,*::after{box-sizing:border-box}
body{margin:0;background:var(--gp-surface-page);color:var(--gp-text-body);font:var(--gp-body-md);-webkit-font-smoothing:antialiased;text-wrap:pretty}
a{color:var(--gp-text-accent);text-decoration:none;transition:color var(--gp-dur-fast) var(--gp-ease-standard)}
a:hover{color:var(--gp-white)}
::selection{background:var(--gp-blue-600);color:var(--gp-white)}
:focus-visible{outline:2px solid var(--gp-focus-ring);outline-offset:2px}
.gp-eyebrow{font:var(--gp-eyebrow);letter-spacing:var(--gp-eyebrow-tracking);text-transform:uppercase;color:var(--gp-text-accent)}
.gp-flag-rule{height:3px;background:var(--gp-gradient-flag)}
/* Saira Condensed has no italic on Google Fonts - every `ital` face we ask for
   comes back upright, so the whole display face is synthetic oblique. Chrome
   paints background-clip:text a fraction short on a synthesised slant, which
   ate the left arm of a trailing T ("RIGHT" rendered as "RIGH1" in the services
   h1). The padding gives the background box the extra sliver to paint into and
   the negative margin gives the space straight back, so nothing moves. */
.gp-chrome-text{background:var(--gp-gradient-chrome);-webkit-background-clip:text;background-clip:text;color:transparent;
  padding-right:.14em;margin-right:-.14em}

/* --- site.css --- */
/* ============================================================
   German Precision Mobile Service — components & layout
   Tokens are concatenated ahead of this file by build.mjs.
   ============================================================ */

/* ---------- primitives ---------- */
.gp-container{max-width:var(--gp-container);margin:0 auto;padding:0 var(--gp-gutter)}
.gp-skip{position:absolute;left:-9999px;top:0;z-index:100;background:var(--gp-action-primary);
  color:#fff;padding:12px 18px;font:var(--gp-label)}
.gp-skip:focus{left:0}
img{max-width:100%;display:block}
/* blockquote and figure default to `margin: 1em 40px`. Inside a masonry
   column that 40px of inline margin pushed the review cards off the page. */
h1,h2,h3,h4,p,ul,ol,figure,blockquote,dl,dd{margin:0}
ul{list-style:none;padding:0}

.gp-display-1{font:var(--gp-display-1);letter-spacing:var(--gp-track-display);text-transform:uppercase;color:var(--gp-text-strong);text-wrap:balance}
.gp-display-2{font:var(--gp-display-2);letter-spacing:var(--gp-track-display);text-transform:uppercase;color:var(--gp-text-strong);text-wrap:balance}
.gp-display-3{font:var(--gp-display-3);letter-spacing:var(--gp-track-display);text-transform:uppercase;color:var(--gp-text-strong);text-wrap:balance}
.gp-h1{font:var(--gp-heading-1);color:var(--gp-text-strong);text-wrap:balance}
.gp-h2{font:var(--gp-heading-2);color:var(--gp-text-strong)}
.gp-h3{font:var(--gp-heading-3);letter-spacing:var(--gp-track-caps);text-transform:uppercase;color:var(--gp-text-strong)}
.gp-body-lg{font:var(--gp-body-lg);color:var(--gp-text-body);max-width:68ch}
.gp-body{font:var(--gp-body-md);color:var(--gp-text-body);max-width:68ch}
.gp-body-sm{font:var(--gp-body-sm);color:var(--gp-text-muted);max-width:68ch}
.gp-mono{font:var(--gp-mono-md);letter-spacing:var(--gp-track-caps);text-transform:uppercase;color:var(--gp-text-muted)}
.gp-mono-sm{font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);text-transform:uppercase;color:var(--gp-text-muted)}
.gp-muted{color:var(--gp-text-muted)}
.gp-strong{color:var(--gp-text-strong)}

/* ---------- icons (Lucide, masked so they inherit currentColor) ---------- */
.gp-icon{display:inline-block;width:20px;height:20px;flex:0 0 auto;background-color:currentColor;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center;
  -webkit-mask-size:contain;mask-size:contain;vertical-align:-4px}
.gp-icon-16{width:16px;height:16px;vertical-align:-3px}
.gp-icon-24{width:24px;height:24px}
.gp-icon-phone{-webkit-mask-image:url(/assets/icons/phone.svg);mask-image:url(/assets/icons/phone.svg)}
.gp-icon-message-square{-webkit-mask-image:url(/assets/icons/message-square.svg);mask-image:url(/assets/icons/message-square.svg)}
.gp-icon-map-pin{-webkit-mask-image:url(/assets/icons/map-pin.svg);mask-image:url(/assets/icons/map-pin.svg)}
.gp-icon-wrench{-webkit-mask-image:url(/assets/icons/wrench.svg);mask-image:url(/assets/icons/wrench.svg)}
.gp-icon-gauge{-webkit-mask-image:url(/assets/icons/gauge.svg);mask-image:url(/assets/icons/gauge.svg)}
.gp-icon-clipboard-check{-webkit-mask-image:url(/assets/icons/clipboard-check.svg);mask-image:url(/assets/icons/clipboard-check.svg)}
.gp-icon-car{-webkit-mask-image:url(/assets/icons/car.svg);mask-image:url(/assets/icons/car.svg)}
.gp-icon-check{-webkit-mask-image:url(/assets/icons/check.svg);mask-image:url(/assets/icons/check.svg)}
.gp-icon-arrow-right{-webkit-mask-image:url(/assets/icons/arrow-right.svg);mask-image:url(/assets/icons/arrow-right.svg)}
.gp-icon-star-filled{-webkit-mask-image:url(/assets/icons/star-filled.svg);mask-image:url(/assets/icons/star-filled.svg)}
.gp-icon-star{-webkit-mask-image:url(/assets/icons/star.svg);mask-image:url(/assets/icons/star.svg)}
.gp-icon-shield{-webkit-mask-image:url(/assets/icons/shield.svg);mask-image:url(/assets/icons/shield.svg)}
.gp-icon-info{-webkit-mask-image:url(/assets/icons/info.svg);mask-image:url(/assets/icons/info.svg)}
.gp-icon-droplet{-webkit-mask-image:url(/assets/icons/droplet.svg);mask-image:url(/assets/icons/droplet.svg)}
.gp-icon-droplets{-webkit-mask-image:url(/assets/icons/droplets.svg);mask-image:url(/assets/icons/droplets.svg)}
.gp-icon-zap{-webkit-mask-image:url(/assets/icons/zap.svg);mask-image:url(/assets/icons/zap.svg)}
.gp-icon-key{-webkit-mask-image:url(/assets/icons/key.svg);mask-image:url(/assets/icons/key.svg)}
.gp-icon-settings{-webkit-mask-image:url(/assets/icons/settings.svg);mask-image:url(/assets/icons/settings.svg)}
.gp-icon-search{-webkit-mask-image:url(/assets/icons/search.svg);mask-image:url(/assets/icons/search.svg)}
.gp-icon-battery-charging{-webkit-mask-image:url(/assets/icons/battery-charging.svg);mask-image:url(/assets/icons/battery-charging.svg)}
.gp-icon-disc-3{-webkit-mask-image:url(/assets/icons/disc-3.svg);mask-image:url(/assets/icons/disc-3.svg)}
.gp-icon-thermometer{-webkit-mask-image:url(/assets/icons/thermometer.svg);mask-image:url(/assets/icons/thermometer.svg)}
.gp-icon-filter{-webkit-mask-image:url(/assets/icons/filter.svg);mask-image:url(/assets/icons/filter.svg)}

/* ---------- button ---------- */
.gp-btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;
  font:var(--gp-label);letter-spacing:var(--gp-track-caps);text-transform:uppercase;
  height:var(--gp-control-h-md);min-height:var(--gp-tap-min);padding:0 20px;
  border-radius:var(--gp-radius-control);border:1px solid transparent;cursor:pointer;
  text-decoration:none;white-space:nowrap;transition:var(--gp-transition-control)}
.gp-btn-sm{height:var(--gp-control-h-sm);padding:0 16px}
.gp-btn-lg{height:var(--gp-control-h-lg);padding:0 24px;font:var(--gp-heading-3);
  letter-spacing:var(--gp-track-caps);text-transform:uppercase}
.gp-btn-primary{background:var(--gp-action-primary);color:var(--gp-white);box-shadow:var(--gp-bevel-up)}
.gp-btn-primary:hover{background:var(--gp-action-primary-hover);color:var(--gp-white)}
.gp-btn-primary:active{background:var(--gp-action-primary-press);box-shadow:var(--gp-bevel-down)}
.gp-btn-secondary{background:transparent;color:var(--gp-chrome-100);border-color:var(--gp-border-subtle)}
.gp-btn-secondary:hover{border-color:var(--gp-border-strong);color:var(--gp-white)}
.gp-btn-full{width:100%}

/* ---------- card / tag ---------- */
.gp-card{position:relative;background:var(--gp-surface-card);border:1px solid var(--gp-border-hairline);
  border-radius:var(--gp-radius-card);padding:var(--gp-space-6);box-shadow:var(--gp-shadow-1);
  transition:box-shadow var(--gp-dur-base) var(--gp-ease-standard),border-color var(--gp-dur-fast) var(--gp-ease-standard)}
.gp-card-interactive:hover{box-shadow:var(--gp-shadow-3);border-color:var(--gp-border-subtle)}
.gp-card-flagged::before{content:"";position:absolute;inset:0 0 auto 0;height:3px;
  background:var(--gp-gradient-flag);border-radius:var(--gp-radius-card) var(--gp-radius-card) 0 0}
.gp-card .gp-icon{color:var(--gp-blue-400)}
.gp-tag{display:inline-flex;align-items:center;height:28px;padding:0 12px;border-radius:var(--gp-radius-pill);
  border:1px solid var(--gp-border-hairline);font:var(--gp-body-sm);color:var(--gp-text-body)}
.gp-tags{display:flex;flex-wrap:wrap;gap:var(--gp-space-2)}

/* ---------- header ---------- */
.gp-header{position:sticky;top:0;z-index:40;background:rgba(7,9,11,.86);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  border-bottom:1px solid var(--gp-border-hairline);
  transition:transform .26s var(--gp-ease-out)}
/* Gets out of the way while you are reading and comes back the moment you
   scroll up. The class goes on <html> rather than the header so the sticky
   rails further down the page can follow it - otherwise they hold their
   108px offset against a header that is no longer there. */
.gp-hdr-hidden .gp-header{transform:translateY(-100%)}
.gp-hdr-hidden .gp-svc-index,.gp-hdr-hidden .gp-contact-card{top:var(--gp-space-6)}
@media (prefers-reduced-motion:reduce){
  .gp-header,.gp-svc-index,.gp-contact-card{transition:none}
}
.gp-header-in{height:76px;display:flex;align-items:center;justify-content:space-between;gap:var(--gp-space-6)}
.gp-logo img{height:50px;width:auto}
.gp-nav{display:flex;align-items:center;gap:var(--gp-space-8)}
.gp-nav a{font:var(--gp-heading-3);letter-spacing:var(--gp-track-caps);text-transform:uppercase;
  color:var(--gp-text-muted);padding-bottom:2px;border-bottom:2px solid transparent}
.gp-nav a:hover{color:var(--gp-text-strong)}
.gp-nav a.is-current{color:var(--gp-text-strong);border-bottom-color:var(--gp-text-accent)}

/* ---------- mobile menu ----------
   The nav was a second full-width row under the logo, which cost ~56px of
   sticky header on every phone screen. It becomes a disclosure panel instead.
   The button only exists when JS is present (.gp-js is set in the head, before
   first paint, so the nav never flashes as a row and then collapses); without
   JS the nav stays exactly as it was. */
.gp-menu-btn{display:none;align-items:center;gap:9px;min-height:var(--gp-tap-min);
  min-width:var(--gp-tap-min);justify-content:center;padding:0 12px;
  background:transparent;border:1px solid var(--gp-border-hairline);
  border-radius:var(--gp-radius-control);color:var(--gp-text-body);cursor:pointer;
  font:var(--gp-label);letter-spacing:var(--gp-track-caps);text-transform:uppercase;
  transition:var(--gp-transition-control)}
.gp-menu-btn:hover{color:var(--gp-text-strong);border-color:var(--gp-border-strong)}
.gp-menu-ico{position:relative;display:block;width:18px;height:2px;border-radius:2px;
  background:currentColor;transition:background .16s var(--gp-ease-out)}
.gp-menu-ico::before,.gp-menu-ico::after{content:"";position:absolute;left:0;
  width:18px;height:2px;border-radius:2px;background:currentColor;
  transition:transform .2s var(--gp-ease-out),top .2s var(--gp-ease-out)}
.gp-menu-ico::before{top:-6px}
.gp-menu-ico::after{top:6px}
.gp-menu-btn[aria-expanded="true"] .gp-menu-ico{background:transparent}
.gp-menu-btn[aria-expanded="true"] .gp-menu-ico::before{top:0;transform:rotate(45deg)}
.gp-menu-btn[aria-expanded="true"] .gp-menu-ico::after{top:0;transform:rotate(-45deg)}

@media (max-width:900px){
  .gp-js .gp-menu-btn{display:inline-flex}
  .gp-js .gp-nav{position:absolute;top:100%;left:0;right:0;z-index:5;
    order:0;width:auto;overflow:visible;
    flex-direction:column;align-items:stretch;gap:0;
    padding:var(--gp-space-1) var(--gp-gutter) var(--gp-space-4);
    background:var(--gp-ink-900);
    border-bottom:1px solid var(--gp-border-hairline);
    box-shadow:0 20px 44px rgba(0,0,0,.5);
    visibility:hidden;opacity:0;transform:translateY(-10px);
    transition:opacity .18s var(--gp-ease-out),transform .22s var(--gp-ease-out),
               visibility 0s linear .22s}
  .gp-js .gp-header.is-nav-open .gp-nav{visibility:visible;opacity:1;transform:none;
    transition-delay:0s,0s,0s}
  /* The current-page marker cannot be the top border here: sitting above the
     row it reads as belonging to the item before it. A left bar, with the text
     still aligned to the gutter. */
  .gp-js .gp-nav a{font-size:18px;padding:15px 0 15px 12px;margin-left:-14px;
    border-bottom:0;border-top:1px solid var(--gp-border-hairline);
    border-left:2px solid transparent}
  .gp-js .gp-nav a:first-child{border-top:0}
  .gp-js .gp-nav a.is-current{border-left-color:var(--gp-text-accent)}
}
@media (prefers-reduced-motion:reduce){
  .gp-js .gp-nav,.gp-menu-ico,.gp-menu-ico::before,.gp-menu-ico::after{transition:none}
}

/* ---------- section scaffolding ---------- */
.gp-section{padding:var(--gp-section-y) 0}
/* ink-850 against ink-900 is five points of lightness — the alternation was
   there in the markup but invisible on screen. ink-800 is already in the ramp
   and the step actually reads. */
.gp-band{background:var(--gp-ink-800);border-top:1px solid var(--gp-border-hairline);
  border-bottom:1px solid var(--gp-border-hairline)}
/* A card on a band is now the same tone as the band, so it stops reading as a
   raised object. --gp-surface-raised exists for exactly this and had never
   been called. */
.gp-band .gp-card,.gp-band .gp-svc-item[open]{background:var(--gp-surface-raised)}
.gp-opener{display:flex;flex-direction:column;gap:var(--gp-space-4);margin-bottom:var(--gp-space-12)}
.gp-opener .gp-flag-rule{width:140px}
.gp-opener .gp-body-lg{max-width:66ch}
.gp-hairline{height:1px;background:var(--gp-border-hairline);border:0}

/* ---------- hero ---------- */
.gp-hero{position:relative;overflow:hidden;isolation:isolate}
.gp-hero-frame{position:absolute;inset:0;z-index:-2;overflow:hidden}
.gp-hero-frame img{width:100%;height:100%;object-fit:cover;object-position:54% 56%;
  transform:scale(1.18);will-change:transform}
/* The hero used to lay a flat rgba(7,9,11,.7) wash over the photograph and
   then a gradient on top of it. Between them any picture collapsed to the
   same dark texture. The wash is now .3 and the darkness is steered: heavy
   on the left where the copy sits, heavy at the foot where the section has
   to meet the marque bar, open through the middle and right. */
.gp-hero::before{content:"";position:absolute;inset:0;z-index:-1;background:rgba(7,9,11,.3)}
.gp-hero::after{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(180deg,rgba(7,9,11,.78) 0%,rgba(7,9,11,.22) 32%,rgba(7,9,11,.5) 76%,rgba(7,9,11,.93) 100%),
             linear-gradient(90deg,rgba(7,9,11,.82) 0%,rgba(7,9,11,.46) 38%,rgba(7,9,11,.05) 70%)}
.gp-hero-in{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(280px,.65fr);
  gap:var(--gp-space-12);padding-block:var(--gp-space-16) var(--gp-space-12);
  /* Proportional rather than fixed: a hero sized in px is either squat on a
     big monitor or overflowing on a small laptop. The floor keeps it from
     collapsing, the cap keeps it from becoming a full-screen splash. */
  min-height:clamp(580px,83vh,880px);align-content:center}
.gp-hero h1{font:var(--gp-display-1);font-size:86px;letter-spacing:var(--gp-track-display);
  text-transform:uppercase;color:var(--gp-text-strong);text-wrap:balance}
/* The h1 was the strongest element on the page and spent all of it on voice —
   no service, no "mobile", nothing but the hook. The deck carries the term
   without costing the line. Denver is deliberately absent: the eyebrow
   directly above already names it and four other cities. */
.gp-hero-deck{display:block;margin-top:var(--gp-space-4);
  font:var(--gp-display-3);font-size:clamp(20px,2.2vw,28px);
  letter-spacing:var(--gp-track-display);color:var(--gp-chrome-100)}
.gp-hero-copy{display:flex;flex-direction:column;gap:var(--gp-space-6);align-items:flex-start}
.gp-hero-copy .gp-flag-rule{width:260px}
.gp-hero-actions{display:flex;flex-wrap:wrap;gap:var(--gp-space-3)}
.gp-hero-reassure{font:var(--gp-body-sm);color:var(--gp-chrome-100);max-width:26ch}
.gp-hero .gp-eyebrow{color:var(--gp-chrome-100)}
.gp-hero-copy .gp-body-lg{color:var(--gp-chrome-100)}

/* spec rail */
.gp-rail{background:rgba(7,9,11,.82);border:1px solid var(--gp-border-subtle);
  border-radius:var(--gp-radius-card);padding:var(--gp-space-5);display:flex;flex-direction:column;gap:var(--gp-space-4)}
.gp-rail-score{display:flex;align-items:baseline;gap:var(--gp-space-3)}
.gp-rail-score .n{font:var(--gp-display-2);letter-spacing:var(--gp-track-display)}
.gp-rail-rows{display:flex;flex-direction:column;gap:var(--gp-space-3)}
.gp-rail-rows li{display:flex;align-items:center;gap:var(--gp-space-3);font:var(--gp-body-sm);color:var(--gp-chrome-100)}
.gp-rail-rows .gp-icon{color:var(--gp-blue-400)}
.gp-rail-specs{display:flex;flex-direction:column;gap:var(--gp-space-2)}

/* ---------- marque bar ---------- */
.gp-marques{display:flex;align-items:center;flex-wrap:wrap;gap:var(--gp-space-6);padding-block:var(--gp-space-6)}
.gp-marque-list{display:flex;flex-wrap:wrap;gap:var(--gp-space-8);flex:1}
.gp-marque-list li{font:var(--gp-heading-3);letter-spacing:var(--gp-track-caps);
  text-transform:uppercase;color:var(--gp-chrome-100)}

/* ---------- generic grids ---------- */
.gp-grid-2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--gp-space-6)}
.gp-grid-3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--gp-space-4)}
.gp-grid-4{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--gp-space-4)}
.gp-split{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,1fr);gap:var(--gp-space-12);align-items:start}
.gp-stack{display:flex;flex-direction:column;gap:var(--gp-space-5)}
.gp-stack-sm{display:flex;flex-direction:column;gap:var(--gp-space-3)}

/* option panels */
.gp-option{display:flex;flex-direction:column;gap:var(--gp-space-3);background:var(--gp-surface-card);
  border:1px solid var(--gp-border-hairline);border-radius:var(--gp-radius-card);padding:var(--gp-space-6)}
.gp-option-head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--gp-space-4)}


/* call/text bar */
.gp-cta-bar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:var(--gp-space-5);background:var(--gp-surface-sunken);border:1px solid var(--gp-border-hairline);
  border-radius:var(--gp-radius-card);padding:var(--gp-space-6) var(--gp-space-8);margin-top:var(--gp-space-10)}
.gp-cta-bar-actions{display:flex;flex-wrap:wrap;gap:var(--gp-space-3)}

/* ---------- process ---------- */
.gp-steps{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--gp-space-8)}
.gp-step{display:flex;flex-direction:column;gap:var(--gp-space-3)}
.gp-step .n{font:var(--gp-display-3);letter-spacing:var(--gp-track-display)}
.gp-note-row{display:flex;flex-wrap:wrap;gap:var(--gp-space-4) var(--gp-space-8);
  justify-content:space-between;padding-top:var(--gp-space-5);margin-top:var(--gp-space-8);
  border-top:1px solid var(--gp-border-hairline)}

/* work photo strip */
.gp-photo-strip{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--gp-space-4);margin-top:var(--gp-space-8)}
.gp-photo{position:relative;border-radius:var(--gp-radius-card);overflow:hidden;
  border:1px solid var(--gp-border-hairline);box-shadow:var(--gp-shadow-2)}
.gp-photo img{width:100%;height:100%;object-fit:cover}
.gp-photo-strip .gp-photo{height:190px}
.gp-photo figcaption{position:absolute;left:12px;bottom:10px;z-index:2;font:var(--gp-mono-sm);
  letter-spacing:var(--gp-track-caps);text-transform:uppercase;color:var(--gp-white)}
.gp-photo-protect::after{content:"";position:absolute;inset:0;z-index:1;background:var(--gp-gradient-protect)}


/* ---------- reviews ---------- */
.gp-stars{display:flex;gap:4px;color:var(--gp-flag-gold)}
/* .gp-card .gp-icon sets colour directly (0,2,0) and a directly-set colour
   always beats one inherited from .gp-stars, so rating stars turned blue
   inside a card. Match the specificity and land later in the file. */
.gp-stars .gp-icon{width:17px;height:17px;color:var(--gp-flag-gold)}
.gp-review{display:flex;flex-direction:column;gap:var(--gp-space-4)}
.gp-review-empty{display:flex;flex-direction:column;gap:var(--gp-space-3);align-items:flex-start;
  border:1px dashed var(--gp-border-subtle);background:transparent;box-shadow:none}

/* ---------- service area ---------- */
.gp-area{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--gp-space-12);align-items:start}
.gp-area .gp-photo{height:400px}
.gp-note{background:var(--gp-surface-sunken);border:1px solid var(--gp-border-hairline);
  border-radius:var(--gp-radius-card);padding:var(--gp-space-5)}

/* ---------- closing CTA ---------- */
.gp-closing{position:relative;border-radius:var(--gp-radius-card);overflow:hidden;
  border:1px solid var(--gp-border-hairline);box-shadow:var(--gp-shadow-2)}
.gp-closing > img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0}
.gp-closing::after{content:"";position:absolute;inset:0;z-index:1;background:var(--gp-gradient-protect);opacity:.96}
.gp-closing-in{position:relative;z-index:2;padding:var(--gp-space-16) var(--gp-space-12);
  display:flex;flex-direction:column;gap:var(--gp-space-5);align-items:flex-start}
.gp-closing-in h2,.gp-closing-in p{color:var(--gp-white)}
.gp-closing-facts{display:flex;flex-wrap:wrap;gap:var(--gp-space-3) var(--gp-space-8)}
.gp-closing-facts li{font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);
  text-transform:uppercase;color:var(--gp-chrome-100)}

/* ---------- footer ---------- */
.gp-footer{background:var(--gp-surface-sunken);border-top:1px solid var(--gp-border-hairline);
  margin-top:var(--gp-space-24);padding-bottom:var(--gp-space-10)}
.gp-footer-grid{display:grid;grid-template-columns:1.3fr .9fr 1fr;gap:var(--gp-space-12);
  padding-top:var(--gp-space-16);padding-bottom:var(--gp-space-12)}
.gp-footer-brand img{height:58px;width:auto;margin-bottom:var(--gp-space-5)}
.gp-footer-brand p{font:var(--gp-body-sm);color:var(--gp-text-muted);max-width:46ch}
.gp-footer-contact,.gp-footer-hours{display:flex;flex-direction:column;gap:var(--gp-space-2);margin-top:var(--gp-space-4)}
.gp-footer-contact a{font:var(--gp-mono-sm);color:var(--gp-chrome-100);letter-spacing:var(--gp-track-caps)}
.gp-footer-contact a:hover{color:var(--gp-white)}
.gp-footer-hours li{font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);
  text-transform:uppercase;color:var(--gp-text-muted)}
.gp-footer-hours .is-closed{color:var(--gp-text-faint)}
.gp-footer-hours-label{margin-top:var(--gp-space-6)}
.gp-footer-area{font:var(--gp-body-sm);color:var(--gp-text-muted);margin-top:var(--gp-space-4)}
.gp-footer-legal{font:var(--gp-body-xs);color:var(--gp-text-faint);
  border-top:1px solid var(--gp-border-hairline);padding-top:var(--gp-space-5);max-width:90ch}

/* ============================================================
   Responsive — the prototypes were desktop-first at 1200px.
   ============================================================ */
@media (max-width:1100px){
  .gp-hero h1{font-size:72px}
  .gp-nav{gap:var(--gp-space-5)}
  .gp-grid-3{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:900px){
  .gp-section{padding:var(--gp-section-y-sm) 0}
  .gp-hero::after{background:linear-gradient(180deg,rgba(7,9,11,.72) 0%,rgba(7,9,11,.42) 26%,rgba(7,9,11,.78) 64%,rgba(7,9,11,.96) 100%)}
  .gp-hero-in{grid-template-columns:1fr;gap:var(--gp-space-8);min-height:0;
    padding-block:var(--gp-space-10) var(--gp-space-8)}
  .gp-hero h1{font-size:clamp(40px,10vw,60px)}
  .gp-display-1{font-size:clamp(38px,9vw,56px)}
  .gp-display-2{font-size:clamp(32px,7.5vw,44px)}
  .gp-display-3{font-size:clamp(26px,6vw,34px)}
  .gp-grid-2,.gp-split,.gp-area,.gp-steps,.gp-photo-strip{grid-template-columns:1fr}
  .gp-photo-strip .gp-photo{height:220px}
  .gp-footer-grid{grid-template-columns:1fr;gap:var(--gp-space-10)}
  .gp-closing-in{padding:var(--gp-space-12) var(--gp-space-6)}
  .gp-header-in{flex-wrap:wrap;height:auto;padding-top:12px;padding-bottom:12px;gap:var(--gp-space-3)}
  .gp-nav:not(.gp-js *){order:3;width:100%;overflow-x:auto;gap:var(--gp-space-5);
    padding-bottom:4px;-webkit-overflow-scrolling:touch;
    scrollbar-width:none;-ms-overflow-style:none}
  .gp-nav::-webkit-scrollbar{display:none}
  .gp-logo img{height:40px}
}
@media (max-width:600px){
  /* shrink the nav so all five links fit without horizontal scrolling */
  .gp-nav{gap:var(--gp-space-4);justify-content:space-between}
  .gp-nav a{font-size:15px;letter-spacing:.02em}
  .gp-header-cta{width:100%;order:2}
  .gp-grid-3,.gp-grid-4{grid-template-columns:1fr}
  .gp-hero-actions .gp-btn,.gp-cta-bar-actions .gp-btn{width:100%}
  .gp-cta-bar{padding:var(--gp-space-5)}
  .gp-marque-list{gap:var(--gp-space-4)}
}

/* Honor reduced motion — the prototype's parallax does not. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms !important;transition-duration:.01ms !important}
  .gp-hero-frame img{transform:none !important}
  html{scroll-behavior:auto}
}

/* ============================================================
   Refinements — second pass over the Claude Design handoff.
   Everything below fixes something that was observed rendering
   wrong, or spends contrast/elevation where it earns its keep.
   ============================================================ */

/* --- container gutter guard ---------------------------------------------
   Several layout classes are applied *to* .gp-container and set their own
   vertical padding. Using the `padding` SHORTHAND there silently zeroes the
   inline gutter, which is invisible at desktop (max-width keeps content off
   the edge) and puts text flush against the screen edge on a phone. Re-assert
   the gutter after every component rule so the bug cannot come back; a
   component that genuinely needs a different gutter can still win with a
   compound selector such as `.gp-container.gp-x`. */
.gp-container{padding-inline:var(--gp-gutter)}

/* --- hero rating rail ---------------------------------------------------
   The score was a 56px number baseline-aligned against a two-line caption,
   which left the caption floating against the digits with no relationship.
   Stack the caption instead and put the stars back where the rating is. */
.gp-rail-score{align-items:center;gap:var(--gp-space-4)}
.gp-rail-score .n{line-height:1}
.gp-rail-score-meta{display:flex;flex-direction:column;gap:7px;min-width:0}
/* Push the spec lines to the foot of the rail so the leftover height reads as
   deliberate structure rather than an unfinished card. */
.gp-rail .gp-hairline:last-of-type{margin-top:auto}

/* --- closing CTA ---------------------------------------------------------
   --gp-gradient-protect is transparent at the top, so the headline sat
   directly on the busiest part of the photograph. Add a left-weighted scrim
   under the copy — the content is left-aligned, so darken where it lives. */
.gp-closing::before{content:"";position:absolute;inset:0;z-index:1;
  background:linear-gradient(90deg,rgba(7,9,11,.93) 0%,rgba(7,9,11,.82) 42%,rgba(7,9,11,.3) 78%,rgba(7,9,11,.18) 100%)}

/* --- mobile action bar ---------------------------------------------------
   Fixed to the bottom on phones. Hidden entirely above 600px. */
/* Long button labels are the reason four of five pages scrolled sideways at
   320px. .gp-btn is white-space:nowrap, so a label like "Not sure if you're
   covered? Call or text" has a min-content width of 357px — and because flex
   and grid items default to min-width:auto, that width propagates all the way
   up and pushes the document past the viewport. Measured at 320: home 381px,
   reviews 372, about 334, contact 328. Let them wrap, and give the fixed
   height back so a second line is not clipped. */
/* Flex and grid items default to min-width:auto, so a child that cannot shrink
   below its min-content width drags the whole document wider than the viewport.
   Bisected at 320px, the sticky contact card was the last 8px on /contact.
   Below 600 every one of these is a single column anyway, so nothing is lost
   by letting them shrink. */
@media (max-width:600px){
  .gp-contact-card,.gp-score,.gp-stack,.gp-hero-copy,.gp-cta-bar,
  .gp-story-copy,.gp-entry-body,.gp-spec-table .row > *{min-width:0}
}

@media (max-width:430px){
  /* min-height stays on --gp-tap-min, not --gp-control-h-md: the md token is
     42px, and swapping it in here quietly dropped the header CTA under the
     44px minimum it had been meeting through the base rule. */
  .gp-btn{white-space:normal;text-align:center;line-height:1.2;
    height:auto;min-height:var(--gp-tap-min);padding-block:11px}
  .gp-btn-lg{height:auto;min-height:var(--gp-control-h-lg);padding-block:13px}
  .gp-btn-sm{height:auto;min-height:var(--gp-tap-min);padding-block:8px}
}

/* ---------- phone rhythm ----------
   Stacked into one column, every section becomes eyebrow + heading + prose and
   the page reads as one slab. Measured, sections are 1.5-2.3 screens tall and
   the band/page alternation - the only structural signal that survives the
   collapse - was 1.16:1, which is invisible on a phone in daylight. */
@media (max-width:600px){
  /* A. Let the band be seen. 1.16 -> 1.48. Surfaces are re-pointed inside the
     band rather than restated: anything using --gp-surface-* picks the local
     value up by inheritance, so a card never ends up darker than its ground. */
  .gp-band{background:var(--gp-ink-600);
    --gp-surface-card:var(--gp-ink-500);
    --gp-surface-raised:var(--gp-ink-500);
    /* sunken points up, not down: on the page ground --gp-ink-850 is already
       lighter than --gp-ink-900, so "sunken" here has always read as a lift.
       Pointing it at ink-700 under a lighter band inverted that and put the
       closing CTA panel in a hole. */
    --gp-surface-sunken:var(--gp-ink-500)}

  /* B. A photo inside the gutter with rounded corners is just another card in
     the stack. Edge to edge it is a break in the page.
     Not negative margins: several of these sit in grid tracks, where a negative
     margin does not pull the track in, it just makes the item wider than its
     track and pushes the document out. /reviews went to 414px that way. Width
     is pinned to the viewport instead, so the item can never be wider than the
     screen whatever its parent is. */
  .gp-entry .gp-photo,.gp-story-cols > .gp-photo,.gp-why-lead .gp-photo,
  .gp-area .gp-photo,.gp-photo-strip .gp-photo{
    width:100vw;max-width:none;margin-left:calc(50% - 50vw);border-radius:0}

  /* C. The strongest differentiator on a phone is the gesture, not the look.
     Tag rows, the facts row and the numbers strip scroll sideways; the page
     itself does not. The partial cell at the right edge is the affordance. */
  .gp-tags,.gp-facts-row{flex-wrap:nowrap;overflow-x:auto;
    margin-inline:calc(-1 * var(--gp-gutter));
    padding-inline:var(--gp-gutter);padding-bottom:6px;
    scrollbar-width:none;-ms-overflow-style:none}
  .gp-tags::-webkit-scrollbar,.gp-facts-row::-webkit-scrollbar{display:none}
  .gp-tags > *,.gp-facts-row > *{flex:0 0 auto}
  /* ...except the 404's way out. A rail hides options off-screen, which is
     the one thing a recovery page cannot do. */
  .gp-tags.gp-recover{flex-wrap:wrap;overflow:visible;margin-inline:0;
    padding-inline:0}
  .gp-numbers{display:grid;grid-auto-flow:column;grid-template-columns:none;
    grid-auto-columns:minmax(46vw,1fr);overflow-x:auto;
    margin-inline:calc(-1 * var(--gp-gutter));
    scrollbar-width:none;-ms-overflow-style:none}
  .gp-numbers::-webkit-scrollbar{display:none}
  .gp-numbers > div{border-left:1px solid var(--gp-border-hairline);border-top:0}
  .gp-numbers > div:first-child{border-left:0}
  /* A nowrap rail has the min-content width of the whole row, and flex and grid
     items default to min-width:auto, so on /services that width climbed the
     entry, the content column and the layout grid and took the document to
     574px. The scroll container has to be allowed to be narrower than what is
     inside it, and so does everything between it and the page. */
  .gp-svc-layout > *,.gp-entry,.gp-entry-body > *,.gp-tags,.gp-facts-row,
  .gp-numbers{min-width:0}
}

/* ---------- A. vary the section opener ----------
   Measured, 23 of 29 sections across the site open with the same flag rule +
   eyebrow + heading stack. On desktop the column layouts differentiate what
   follows; stacked on a phone that opener IS the section, so you meet the same
   thing eight times on the home page. Three treatments in rotation instead of
   one. Rotation is by position, not by hand, so new sections inherit it. */
@media (max-width:600px){
  /* 2nd of every three: the rule runs the full width as a chapter divider and
     the kicker sits on it. */
  main > section:nth-of-type(3n+2) .gp-flag-rule{width:100%!important}
  main > section:nth-of-type(3n+2) .gp-eyebrow{
    display:inline-block;margin-top:var(--gp-space-1)}

  /* 3rd of every three: no rule at all, the kicker carries a left bar and the
     heading sits tight under it. */
  main > section:nth-of-type(3n) .gp-flag-rule{display:none}
  main > section:nth-of-type(3n) .gp-eyebrow{
    border-left:3px solid var(--gp-text-accent);padding-left:10px;
    font-size:13px;letter-spacing:.14em}
  main > section:nth-of-type(3n) .gp-opener,
  main > section:nth-of-type(3n) .gp-stack-sm{gap:var(--gp-space-3)}
}

/* ---------- D. scroll progress ----------
   Orientation on a page that is twenty-one screens tall. Scroll-driven, so no
   JS and no scroll listener; where animation-timeline is unsupported the bar
   simply never appears. Above the header so it survives the header hiding. */
.gp-progress{display:none}
@supports (animation-timeline:scroll()){
  @media (max-width:900px){
    .gp-progress{display:block;position:fixed;inset:0 0 auto 0;height:2px;z-index:45;
      background:var(--gp-action-primary);transform-origin:0 50%;
      transform:scaleX(0);pointer-events:none;
      animation:gp-progress linear both;animation-timeline:scroll()}
    @keyframes gp-progress{to{transform:scaleX(1)}}
  }
}
@media (prefers-reduced-motion:reduce){
  .gp-progress{display:none}
}

/* Floating call button. The bar it replaces was 62px of every screen, for good,
   plus a matching body padding so it never sat on the footer. A 60px circle in
   the corner keeps the tap target and gives the strip back. */
.gp-fab{display:none}
@media (max-width:600px){
  .gp-fab{position:fixed;z-index:50;right:16px;
    bottom:calc(18px + env(safe-area-inset-bottom,0px));
    display:inline-flex;align-items:center;justify-content:center;
    width:60px;height:60px;border-radius:50%;
    background:var(--gp-action-primary);color:var(--gp-white);
    border:1px solid rgba(255,255,255,.16);
    box-shadow:0 12px 28px rgba(0,0,0,.55),var(--gp-bevel-up);
    transition:var(--gp-transition-control)}
  .gp-fab .gp-icon{width:26px;height:26px;vertical-align:0}
  .gp-fab:active{background:var(--gp-action-primary-press)}
  /* A ring pulses out of it rather than the button throbbing continuously:
     the cycle is 3.6s and the movement occupies the first fifth of it, so it
     reads as an occasional signal instead of a permanent flash in the corner.
     Transform and opacity only, so it composites and costs no layout. */
  .gp-fab{animation:gp-fab-nudge 3.6s var(--gp-ease-out) infinite}
  .gp-fab::after{content:"";position:absolute;inset:-2px;border-radius:50%;
    border:2px solid var(--gp-action-primary);pointer-events:none;
    opacity:0;animation:gp-fab-ring 3.6s var(--gp-ease-out) infinite}
  /* Only enough to keep the circle off the footer's last line. */
  body{padding-bottom:calc(36px + env(safe-area-inset-bottom,0px))}
}
/* Has to come after the block above, not with the other reduced-motion rules
   240 lines up: same specificity, and the later rule wins. Measured — with the
   override up there the computed animation-name on .gp-fab still came back
   gp-fab-nudge under prefers-reduced-motion. */
@media (prefers-reduced-motion:reduce){
  .gp-fab{animation:none}
  .gp-fab::after{content:none}
}

/* --- mobile header -------------------------------------------------------
   The header was wrapping to three rows (logo / full-width CTA / nav), eating
   ~170px above the fold on the device that matters most. Two rows, and the
   CTA shrinks to sit beside the logo. */
@media (max-width:600px){
  .gp-header-cta{width:auto;order:0;padding:0 14px;font-size:12px;height:var(--gp-control-h-sm)}
  .gp-header-in{gap:var(--gp-space-2);padding-top:10px;padding-bottom:8px}
  .gp-nav{order:3;gap:var(--gp-space-3)}
  .gp-nav a{font-size:14px}
}
/* With the menu button in the bar, logo + phone CTA + button measured 350px of
   content against 272 at 320px, so the bar still wrapped to two rows and the
   header stayed 144px tall. Below 600 the fixed call bar already puts Call and
   Text under the thumb, so the header CTA is redundant there: drop it and the
   bar is one row of logo + button. 144px -> 90px of sticky header. */
@media (max-width:600px){
  .gp-js .gp-header-cta{display:none}
  .gp-js .gp-header-in{flex-wrap:nowrap}
}

/* --- hairlines -----------------------------------------------------------
   <hr> carries a default 1px inset border and a 0.5em block margin, so every
   `.gp-hairline` rendered as a 3px ridge with unplanned space around it —
   which is why the rail's dividers read as gaps rather than rules. */
.gp-hairline{border:0;margin:0;display:block;width:100%}

/* --- marque bar on phones ------------------------------------------------
   Centring a one-line label against a three-line wrapped list left the label
   floating in the middle of the marques. Stack it. */
@media (max-width:600px){
  .gp-marques{flex-direction:column;align-items:flex-start;gap:var(--gp-space-3)}
  .gp-marque-list{gap:var(--gp-space-3) var(--gp-space-6)}
}

/* The rail stretched to the full height of a three-line hero headline, which
   left ~230px of nothing inside a bordered card. Let it hug its content and
   give the photograph the rest of the column. */
@media (min-width:901px){ .gp-rail{align-self:start} }

/* --- tap targets ---------------------------------------------------------
   Measured on a 390px viewport: nav links were 22px tall, the footer's phone
   and email links 15px, and the eleven "call or text" links on Services 24px.
   Those are the conversion links on a site whose traffic is almost entirely
   phones. Grow the hit area without moving anything visually. */
.gp-nav a{padding-block:6px}                       /* 22px -> 34px, desktop too */
@media (max-width:900px){
  .gp-nav a{padding-block:13px}                    /* measured 43.98 at 12 -> 46 */
  .gp-header-in{padding-bottom:0}
  .gp-nav{padding-bottom:0}
  .gp-logo{display:inline-flex;align-items:center;padding-block:5px}
  .gp-footer-contact{gap:0}
  .gp-footer-contact a,.gp-entry-cta,.gp-contact-card a.gp-mono-sm{
    display:inline-flex;align-items:center;min-height:44px}
}

/* Mono is uppercased by default for spec labels, but an address is not a
   label — KEVIN@GERMANPRECISIONMOBILE.COM reads as a typo. */
a[href^="mailto:"]{text-transform:none}

/* The last section already ends with 96px of padding, so the footer's own
   96px top margin stacked into ~190px of dead band above it. */
.gp-footer{margin-top:var(--gp-space-10)}

/* .gp-stack is a flex column, so a button inside it stretched to the full
   column width — an 800px "Read all reviews on Google" bar. Buttons size to
   their label; only .gp-btn-full opts out. */
.gp-stack > .gp-btn:not(.gp-btn-full),
.gp-stack-sm > .gp-btn:not(.gp-btn-full){align-self:flex-start}

/* A transparent outline button sitting on a photograph reads as a border, not
   a control. Give the hero's secondary action a ground of its own. */
.gp-hero-actions .gp-btn-secondary,.gp-closing .gp-btn-secondary{
  background:rgba(12,16,19,.72);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.gp-hero-actions .gp-btn-secondary:hover,.gp-closing .gp-btn-secondary:hover{background:rgba(26,33,38,.85)}

/* ============================================================
   Header utility strip + condensing bar
   ============================================================ */
/* Flag graphite — the same tone as the first segment of the flag rule that
   sits directly under the header, so the bar is bracketed top and bottom by
   one colour. It also hands the blue back to the call button: the design
   system asks for a single accent doing all the CTA work, and a blue band,
   a blue button and a blue nav underline was three. */
.gp-util{background:var(--gp-flag-graphite);border-bottom:1px solid rgba(255,255,255,.1);
  height:32px;overflow:hidden}
.gp-util-in{display:flex;align-items:center;justify-content:space-between;
  gap:var(--gp-space-6);height:32px}
.gp-util span{font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);
  text-transform:uppercase;color:var(--gp-chrome-200);white-space:nowrap}
.gp-util .is-lit{color:var(--gp-chrome-100)}
/* (0,1,0) lost to `.gp-util span` at (0,1,1), so the gold never applied —
   the rating has been rendering in the muted grey since the strip went in. */
.gp-util .gp-util-rating{color:var(--gp-flag-gold)}

/* The bar gives its height back once you start reading: the utility strip
   collapses first, then the main row and the logo settle to a compact size.
   Scroll-driven animation is Chromium-only for now; everywhere else the
   header simply stays at full height, which is what it does today. */
@supports (animation-timeline:scroll()){
  .gp-util{animation:gp-util-collapse linear both;
    animation-timeline:scroll();animation-range:0 150px}
  /* Only the single-row desktop bar condenses. Below 900px .gp-header-in
     wraps to two rows with height:auto, and pinning it to a fixed height
     clips the nav row straight out of the header. */
  @media (min-width:901px){
    .gp-header-in{animation:gp-header-condense linear both;
      animation-timeline:scroll();animation-range:0 220px}
    .gp-logo img{animation:gp-logo-condense linear both;
      animation-timeline:scroll();animation-range:0 220px}
  }
}
@keyframes gp-fab-ring{
  0%{opacity:.6;transform:scale(1)}
  20%{opacity:0;transform:scale(1.7)}
  100%{opacity:0;transform:scale(1.7)}
}
@keyframes gp-fab-nudge{
  0%,16%,100%{transform:scale(1)}
  7%{transform:scale(1.07)}
}
@keyframes gp-util-collapse{to{height:0;opacity:0}}
@keyframes gp-header-condense{to{height:62px}}
@keyframes gp-logo-condense{to{height:38px}}

/* 760 was a guess; measured, the three items need 744px plus gutters, so the
   rating was being clipped by the strip's overflow:hidden up to about 790. */
@media (max-width:800px){
  /* one line, and only the fact that actually changes a decision */
  .gp-util,.gp-util-in{height:28px}
  .gp-util-sm-hide{display:none}
  /* Was 11px with a 330px breakpoint bumping it to 12, because the old hours
     string needed 277px and a 320px phone has 272 between the gutters. The
     corrected hours are shorter — 254px — so 12px now fits on the narrowest
     phone and the split is gone. */
  .gp-util span{font-size:12px}
}

/* A moving header is motion. Hold it still and keep every fact on screen. */
@media (prefers-reduced-motion:reduce){
  .gp-util,.gp-header-in,.gp-logo img{animation:none !important}
}

/* Where two sections on the same ground meet there was no seam at all. This
   is --gp-gradient-hairline, defined in the tokens and never used: a rule
   that fades out at both ends, so it separates without drawing a box. */
.gp-section:not(.gp-band) + .gp-section:not(.gp-band){position:relative}
.gp-section:not(.gp-band) + .gp-section:not(.gp-band)::before{
  content:"";position:absolute;top:0;left:0;right:0;height:1px;
  background:var(--gp-gradient-hairline)}

/* ============================================================
   Surface textures — two, used for different weights of section.

   Measured off the client's references rather than guessed: the line grid
   there runs blue-tinted on a navy ground with a wash falling from the top
   left, and its own label reads GRID-SQ 180PX. The dot grid is far quieter
   than it looks — a lift of under 5 over its ground.
   ============================================================ */

/* --- blueprint: one uniform grid, plus the wash --------------------------
   A single cell size, every line the same weight. An earlier version had
   minors inside majors — squares inside squares — which reads as two grids
   fighting rather than as one surface. The wash stays: it is the part of the
   reference that was working. */
.gp-blueprint{position:relative}
.gp-blueprint::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background-image:
    linear-gradient(rgba(61,155,224,.075) 1px,transparent 1px),
    linear-gradient(90deg,rgba(61,155,224,.075) 1px,transparent 1px);
  background-size:28px 28px;
  /* Locked to the service list: a closed row is 56px, so two cells fill one
     row exactly, and the list starts 346px into the section — 346 mod 28 is
     9.7 — so this offset drops a line on every row boundary instead of
     through the middle of the text. The 345.7 holds from 1000px to 1920px. */
  background-position:0 9.7px;
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 52%,transparent 96%);
          mask-image:linear-gradient(180deg,#000 0%,#000 52%,transparent 96%)}
/* the wash, falling from the top left as the reference does */
.gp-blueprint::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(120% 82% at 16% 0%,rgba(15,98,168,.20) 0%,rgba(15,98,168,.05) 42%,transparent 72%)}
.gp-blueprint > *{position:relative}

/* --- dot grid: the quiet one, for sections that only need a surface ----- */
.gp-dotgrid{position:relative}
.gp-dotgrid::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background-image:radial-gradient(rgba(182,195,204,.11) 1px,transparent 1.4px);
  background-size:32px 32px;
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 62%,transparent 100%);
          mask-image:linear-gradient(180deg,#000 0%,#000 62%,transparent 100%)}
.gp-dotgrid > *{position:relative}

@media (max-width:600px){
  .gp-blueprint::before{background-size:40px 40px}
  .gp-dotgrid::before{background-size:24px 24px}
}
}
@media (max-width:600px){
  .gp-blueprint::before{background-size:20px 20px}
}

/* --- pages.css --- */
/* ============================================================
   Page-specific layouts — Services, About, Reviews, Contact
   ============================================================ */

/* ---------------- About ---------------- */

/* Mosaic hero. The plate is a single composited image rather than twelve tiles
   - see tools/make-hero-mosaic.mjs for why. The scrim is weighted to the left
   so the copy sits on a clean ground while the photographs keep some life on
   the right, which also rhymes with the services hero. 620px so the tiles read
   as photographs; at 504 the old hero height they were cropped to fragments. */
.gp-hero-mosaic{position:relative;overflow:hidden;isolation:isolate;
  min-height:620px;display:grid;align-content:center}
.gp-hero-mosaic-bg{position:absolute;inset:0;z-index:-2}
.gp-hero-mosaic-bg img{width:100%;height:100%;object-fit:cover;object-position:center}
.gp-hero-mosaic::after{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:
    linear-gradient(90deg,rgba(7,9,11,.95) 0%,rgba(7,9,11,.88) 46%,rgba(7,9,11,.60) 100%),
    linear-gradient(180deg,rgba(7,9,11,.62) 0%,rgba(7,9,11,.26) 40%,rgba(7,9,11,.84) 100%)}
.gp-hero-mosaic-in{position:relative;z-index:1}
.gp-hero-mosaic-in h1{max-width:min(940px,66vw)}
.gp-hero-mosaic-in .gp-body-lg{max-width:min(720px,54vw)}

/* ---------------- Services ---------------- */

/* Hero: the photo bleeds off the right edge rather than sitting in a box.
   Two masks intersected - one fades the left edge into the ground, one takes
   the top and bottom off - so there is no rectangle anywhere on screen, only
   the photo dissolving into the page. The scrim lives on the section rather
   than inside the mask, so the copy keeps its ground even where the photo has
   already faded to nothing. */
.gp-hero-edge-sec{position:relative;overflow:hidden;isolation:isolate}
.gp-hero-edge{position:absolute;inset:0 0 0 auto;width:46vw;z-index:-2;pointer-events:none;
  -webkit-mask-image:
    linear-gradient(90deg,transparent 0%,rgba(0,0,0,.16) 24%,rgba(0,0,0,.70) 54%,#000 84%),
    linear-gradient(180deg,transparent 0%,#000 20%,#000 70%,transparent 100%);
  -webkit-mask-composite:source-in;
  mask-image:
    linear-gradient(90deg,transparent 0%,rgba(0,0,0,.16) 24%,rgba(0,0,0,.70) 54%,#000 84%),
    linear-gradient(180deg,transparent 0%,#000 20%,#000 70%,transparent 100%);
  mask-composite:intersect}
.gp-hero-edge img{width:100%;height:100%;object-fit:cover;object-position:52% 62%;
  filter:saturate(.9) brightness(.88)}
.gp-hero-edge-sec::after{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:linear-gradient(90deg,var(--gp-ink-900) 0%,rgba(7,9,11,.74) 40%,rgba(7,9,11,0) 74%)}
/* The copy column has to shrink with the viewport, not just cap out: at a flat
   700px the last line of body copy ran into the photo at 901px and measured
   4.29:1 against the pixels actually behind it. */
.gp-hero-edge-in .gp-stack{max-width:min(700px,60vw)}

/* "Why it matters": the marques run edge to edge as a masthead rule, the
   argument sits in two columns beneath. Auto columns with space-between so the
   row actually spans the container instead of trailing off half way. The li
   takes the display token directly, so it needs its own responsive size - the
   .gp-display-3 class rule does not reach it. */
/* The constraint belongs on the h2, not the wrapper: ch on the wrapper resolves
   against the inherited body font, which capped it near 300px and stacked the
   heading four lines deep. Unconstrained it sets on one line down to about
   1000px and reads as a masthead over the marque rule. */
.gp-why-head h2{max-width:none}
.gp-marque-row{list-style:none;display:grid;grid-template-columns:repeat(6,auto);
  justify-content:space-between;gap:var(--gp-space-4) var(--gp-space-6);
  margin-top:var(--gp-space-8);padding:var(--gp-space-8) 0;
  border-top:1px solid var(--gp-border-hairline);
  border-bottom:1px solid var(--gp-border-hairline)}
.gp-marque-row li{font:var(--gp-display-3);font-size:clamp(24px,2.7vw,42px);
  letter-spacing:var(--gp-track-display);
  background:var(--gp-gradient-chrome);-webkit-background-clip:text;background-clip:text;
  color:transparent;padding-right:.14em;margin-right:-.14em}
.gp-marque-also{display:flex;flex-wrap:wrap;align-items:baseline;min-width:0;
  gap:var(--gp-space-2) var(--gp-space-5);padding:var(--gp-space-5) 0;
  border-bottom:1px solid var(--gp-border-hairline)}
.gp-marque-also ul{list-style:none;display:flex;flex-wrap:wrap;min-width:0;
  gap:var(--gp-space-2) var(--gp-space-5);margin:0;padding:0}
.gp-marque-also li{font:var(--gp-display-3);font-size:clamp(15px,1.5vw,20px);
  letter-spacing:var(--gp-track-display);color:var(--gp-chrome-100)}
.gp-why-body{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--gp-space-10);margin-top:var(--gp-space-8);align-items:start}
/* .gp-display-1 holds 76px all the way down to 900, which in a column this
   narrow stacked the headline eight lines deep and made the hero 940px tall -
   taller than the empty version it replaced. Scale it with the column, but only
   on desktop: below 900 the site's own clamp(38px,9vw,56px) already handles it
   and this one would floor the headline at 52px on a phone. */
@media (min-width:901px){
  .gp-hero-edge-in h1{font-size:clamp(52px,5.8vw,76px)}
}

.gp-svc-layout{display:grid;grid-template-columns:230px minmax(0,1fr);gap:var(--gp-space-16);align-items:start}
.gp-svc-index{position:sticky;top:108px;display:flex;flex-direction:column;gap:var(--gp-space-2);
  transition:top .26s var(--gp-ease-out)}
.gp-svc-index a:not(.gp-btn){display:flex;gap:10px;align-items:baseline;font:var(--gp-body-sm);color:var(--gp-text-muted);
  border-left:2px solid transparent;padding:6px 0 6px 12px;transition:var(--gp-transition-control)}
.gp-svc-index a:not(.gp-btn):hover{color:var(--gp-text-body)}
.gp-svc-index a.is-active:not(.gp-btn){color:var(--gp-text-strong);border-left-color:var(--gp-text-accent)}
.gp-svc-index .n{font:var(--gp-mono-sm);color:var(--gp-text-accent)}
.gp-svc-index .gp-btn{margin-top:var(--gp-space-4)}

/* Clear the sticky header on an anchor jump. The header only condenses on
   desktop, so a phone has to clear the full two-row bar — 120px left a
   deep-linked heading 5px underneath it. */
.gp-entry{scroll-margin-top:124px;padding-bottom:var(--gp-space-16)}
.gp-entry + .gp-entry{border-top:1px solid var(--gp-border-hairline);padding-top:var(--gp-space-16)}
.gp-entry-head{display:flex;flex-wrap:wrap;gap:var(--gp-space-4);align-items:baseline;
  border-bottom:1px solid var(--gp-border-subtle);padding-bottom:var(--gp-space-4);margin-bottom:var(--gp-space-5)}
.gp-entry-head .n{font:var(--gp-display-3);letter-spacing:var(--gp-track-display)}
.gp-entry-head h2{flex:1;min-width:240px}
.gp-entry-seo{display:block;width:100%;font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);
  text-transform:uppercase;color:var(--gp-text-faint)}
.gp-entry-body{display:flex;flex-direction:column;gap:var(--gp-space-6)}
/* One skeleton for all eleven entries: the checklist on the left, the photo
   (plus the price card, where there is one) on the right. This replaced an
   auto-fit grid whose markup decided per entry whether the photo went left,
   right, full-width or nowhere - eleven layouts instead of one. Proportional
   columns rather than a fixed rail so the pair never gets squeezed against the
   sticky index; it collapses at the same 900px breakpoint the index does. */
.gp-entry-main{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);
  gap:var(--gp-space-8);align-items:start}
.gp-entry-aside{display:flex;flex-direction:column;gap:var(--gp-space-4)}
.gp-check{display:flex;flex-direction:column;gap:10px}
.gp-check li{display:flex;gap:10px;align-items:flex-start;font:var(--gp-body-sm);color:var(--gp-text-body)}
.gp-check .gp-icon{color:var(--gp-blue-400);margin-top:3px}
.gp-sublabel{display:block;font:var(--gp-eyebrow);letter-spacing:var(--gp-eyebrow-tracking);
  text-transform:uppercase;color:var(--gp-text-muted);margin-bottom:var(--gp-space-3)}
/* "What's included" heads the checklist in all eleven service entries. Now
   that every entry shares one skeleton the label is doing no work on a phone -
   a checklist under a service heading is self-evident - and eleven repetitions
   of it is a third of the repeated text on the page. Hidden visually only:
   still in the accessibility tree, so the list keeps its name for a screen
   reader. Only inside .gp-entry-main, so "Common signs you need this" below
   the fold of each entry is untouched. */
@media (max-width:600px){
  .gp-entry-main .gp-sublabel{position:absolute;width:1px;height:1px;
    padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);
    white-space:nowrap;border:0}
}
.gp-price-card{display:flex;flex-direction:column;gap:var(--gp-space-2);background:var(--gp-surface-card);
  border:1px solid var(--gp-border-hairline);border-radius:var(--gp-radius-card);padding:var(--gp-space-6)}
.gp-price-card .fig{font:var(--gp-display-3);letter-spacing:var(--gp-track-display)}
.gp-entry-note{background:var(--gp-surface-sunken);border:1px solid var(--gp-border-hairline);
  border-radius:var(--gp-radius-card);padding:var(--gp-space-5)}
.gp-entry-cta{display:inline-flex;align-items:center;gap:10px;margin-top:var(--gp-space-2);
  font:var(--gp-heading-3);letter-spacing:var(--gp-track-caps);text-transform:uppercase;color:var(--gp-text-accent)}
/* B. /services ends every one of its eleven entries with the same
   "Call or Text: (720) 917-4601" line. Below 600 the floating call button is on
   screen permanently, so those eleven are pure repetition; above 600 there is
   no floating button, so they stay. This has to live here, not in site.css:
   pages.css bundles last, so at equal specificity site.css loses. */
@media (max-width:600px){
  /* Not all eleven: hiding the lot left a 16-screen stretch of /services with
     nothing tappable in the flow. Every fourth entry keeps one, plus the last,
     so there is an anchor every few entries and three lines instead of eleven. */
  .gp-entry:not(:nth-of-type(4n)):not(:last-of-type) .gp-entry-cta{display:none}
}
.gp-entry .gp-photo{height:260px}

/* The photos are 3:4 phone frames dropped into a landscape slot, so the default
   centre crop lands on sky or driveway for the ones shot from a distance.
   These pull the crop down to where the car or the part actually sits. */
#alternator .gp-photo img{object-position:center 58%}
#battery .gp-photo img{object-position:center 66%}
#starter .gp-photo img{object-position:center 78%}
#oil .gp-photo img{object-position:center 68%}
#ppi .gp-photo img{object-position:center 76%}

.gp-spec-table{display:flex;flex-direction:column}
.gp-spec-table .row{display:grid;grid-template-columns:200px minmax(0,1fr);gap:var(--gp-space-5);
  padding:var(--gp-space-4) 0;border-top:1px solid var(--gp-border-hairline)}
.gp-spec-table .row:first-child{border-top:0}
.gp-spec-table .k{font:var(--gp-eyebrow);letter-spacing:var(--gp-eyebrow-tracking);
  text-transform:uppercase;color:var(--gp-text-muted)}
.gp-spec-table .v{font:var(--gp-body-md);color:var(--gp-text-body)}
.gp-spec-table .v b{font:var(--gp-mono-md);color:var(--gp-text-strong);letter-spacing:var(--gp-track-caps)}
.gp-warranty{display:flex;flex-direction:column;gap:var(--gp-space-3);align-items:center;text-align:center;
  background:var(--gp-surface-card);border:1px solid var(--gp-border-hairline);
  border-radius:var(--gp-radius-card);padding:var(--gp-space-8)}
.gp-warranty .fig{font:var(--gp-display-2);letter-spacing:var(--gp-track-display)}

/* ---------------- About ---------------- */
.gp-facts-row{display:flex;flex-wrap:wrap;gap:var(--gp-space-4) var(--gp-space-8)}
.gp-facts-row li{font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);
  text-transform:uppercase;color:var(--gp-text-muted)}
/* Was a three-column grid whose first track was a 200px rail holding only the
   eyebrow. The heading is its own full-width opener now and the photo takes the
   left column, which is the only thing on this page giving the story a face. */
.gp-story{display:block}
.gp-story-open{display:flex;flex-direction:column;gap:var(--gp-space-3);
  margin-bottom:var(--gp-space-10)}
.gp-story-cols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.3fr);
  gap:var(--gp-space-12);align-items:start}
.gp-story-copy{display:flex;flex-direction:column;gap:var(--gp-space-5)}
.gp-story-copy em{color:var(--gp-text-strong)}
.gp-story-cols > .gp-photo{height:440px}
.gp-pullquote{position:relative;display:flex;flex-direction:column;gap:var(--gp-space-4);
  background:var(--gp-surface-card);border:1px solid var(--gp-border-hairline);
  border-radius:var(--gp-radius-card);padding:var(--gp-space-8) var(--gp-space-6) var(--gp-space-6)}
.gp-pullquote::before{content:"";position:absolute;inset:0 0 auto 0;height:3px;
  background:var(--gp-gradient-flag);border-radius:var(--gp-radius-card) var(--gp-radius-card) 0 0}
/* The quote used to sit under the photo, which made the left column ~180px
   taller than the copy and left a hole at the foot of the right one. Balancing
   the two would have needed a 193px photo - a strip. Full width instead leaves
   photo 440 against copy 415, near enough level, and the quote gets to be a
   statement rather than a sidebar.
   This has to sit AFTER .gp-pullquote: same (0,1,0) specificity, so placed
   before it the display:grid lost to display:flex and align-items:end pushed
   both children to the right edge of a flex column. */
.gp-story-quote{margin-top:var(--gp-space-10);display:grid;
  grid-template-columns:minmax(0,1.7fr) minmax(0,1fr);
  gap:var(--gp-space-6) var(--gp-space-10);align-items:end}
.gp-story-quote p{font-size:clamp(22px,2.1vw,30px)}
.gp-values{display:flex;flex-direction:column}
.gp-value{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.6fr);gap:var(--gp-space-12);
  padding:var(--gp-space-10) 0;border-top:1px solid var(--gp-border-hairline);align-items:start}
.gp-value:first-child{border-top:0;padding-top:0}
/* The even rows used to mirror, but `order` moves the children and leaves the
   grid tracks where they are - so on 02 and 04 the wide 1.6fr column held a
   three-word heading while a 210-character paragraph was squeezed into the
   1fr one, and a sighted reader met the paragraph before its heading while a
   screen reader got them the right way round. Same column every row now. */
.gp-value-key{display:flex;flex-direction:column;gap:var(--gp-space-3)}
.gp-value-key .n{font:var(--gp-mono-md);color:var(--gp-text-accent);letter-spacing:var(--gp-track-caps)}
.gp-numbers{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));
  border:1px solid var(--gp-border-hairline);border-radius:var(--gp-radius-card);overflow:hidden}
.gp-numbers > div{display:flex;flex-direction:column;gap:var(--gp-space-3);align-items:center;text-align:center;
  padding:var(--gp-space-8) var(--gp-space-5);border-left:1px solid var(--gp-border-hairline)}
.gp-numbers > div:first-child{border-left:0}
.gp-numbers .fig{font:var(--gp-display-2);letter-spacing:var(--gp-track-display)}
.gp-numbers .lab{font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);
  text-transform:uppercase;color:var(--gp-text-muted)}

/* ---------------- Reviews ---------------- */
.gp-reviews-hero{display:grid;grid-template-columns:300px minmax(0,1fr);gap:var(--gp-space-12);align-items:center}
.gp-score{display:flex;flex-direction:column;gap:var(--gp-space-3);align-items:center;text-align:center;
  background:var(--gp-surface-card);border:1px solid var(--gp-border-hairline);
  border-radius:var(--gp-radius-card);padding:var(--gp-space-8);box-shadow:var(--gp-shadow-2)}
.gp-score .fig{font:var(--gp-display-1);letter-spacing:var(--gp-track-display);line-height:1}
.gp-score .gp-stars .gp-icon{width:18px;height:18px}
.gp-stats-strip{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--gp-space-6)}
.gp-stats-strip > div{display:flex;flex-direction:column;gap:var(--gp-space-2)}
.gp-stats-strip .fig{font:var(--gp-heading-1);color:var(--gp-text-strong)}
.gp-masonry{column-count:3;column-gap:20px}
.gp-masonry > *{break-inside:avoid;margin-bottom:20px;display:inline-block;width:100%}
.gp-theme{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.5fr);gap:var(--gp-space-10);
  padding:var(--gp-space-10) 0;border-top:1px solid var(--gp-border-hairline);align-items:start}
.gp-theme:first-child{border-top:0;padding-top:0}
.gp-two-up{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--gp-space-6)}
.gp-two-up > div{display:flex;flex-direction:column;gap:var(--gp-space-4);align-items:flex-start;
  padding:var(--gp-space-8);border-radius:var(--gp-radius-card)}
.gp-two-up .bordered{border:1px solid var(--gp-border-hairline)}
.gp-two-up .sunken{background:var(--gp-surface-sunken);border:1px solid var(--gp-border-hairline)}

/* ---------------- Contact ---------------- */
.gp-contact-hero{display:grid;grid-template-columns:minmax(0,1fr) minmax(360px,.8fr);
  gap:var(--gp-space-12);align-items:start}
.gp-contact-card{position:sticky;top:108px;transition:top .26s var(--gp-ease-out);display:flex;flex-direction:column;gap:var(--gp-space-5);
  background:var(--gp-surface-card);border:1px solid var(--gp-border-subtle);
  border-radius:var(--gp-radius-card);padding:var(--gp-space-8);box-shadow:var(--gp-shadow-2)}
/* 40px tall on a touch viewport - the one control on the site still under the
   44px minimum, and it is the primary conversion link on the contact page. */
.gp-contact-card .number{display:block;min-height:var(--gp-tap-min);
  display:flex;align-items:center;font:var(--gp-display-3);letter-spacing:var(--gp-track-display);
  color:var(--gp-text-strong)}
.gp-contact-card .number:hover{color:var(--gp-blue-400)}
.gp-contact-actions{display:flex;flex-direction:column;gap:var(--gp-space-3)}
.gp-kv{display:flex;flex-direction:column;gap:var(--gp-space-2)}
.gp-kv .row{display:flex;justify-content:space-between;gap:var(--gp-space-4);align-items:baseline}
.gp-kv .k{font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);text-transform:uppercase;color:var(--gp-text-muted)}
.gp-kv .v{font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);text-transform:uppercase;color:var(--gp-text-body)}
.gp-process{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  border:1px solid var(--gp-border-hairline);border-radius:var(--gp-radius-card);overflow:hidden}
.gp-process > div{display:flex;flex-direction:column;gap:var(--gp-space-3);
  padding:var(--gp-space-8) var(--gp-space-6);border-left:1px solid var(--gp-border-hairline)}
.gp-process > div:first-child{border-left:0}
.gp-process .n{font:var(--gp-display-3);letter-spacing:var(--gp-track-display)}

/* ============================================================
   Scroll reveal — progressive enhancement only.
   Content is hidden ONLY after JS confirms it can reveal it, so
   crawlers and no-JS visitors always get the final readable state.
   ============================================================ */
html.js-reveal [data-reveal]{opacity:0;transform:translateY(12px);
  transition:opacity 420ms var(--gp-ease-out),transform 420ms var(--gp-ease-out)}
html.js-reveal [data-reveal].is-in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
  html.js-reveal [data-reveal]{opacity:1 !important;transform:none !important;transition:none}
}

/* ---------------- responsive ---------------- */
@media (max-width:1100px){
  .gp-masonry{column-count:2}
  .gp-numbers{grid-template-columns:repeat(3,minmax(0,1fr))}
  .gp-numbers > div:nth-child(3n+1){border-left:0}
  .gp-process{grid-template-columns:repeat(2,minmax(0,1fr))}
  .gp-process > div:nth-child(odd){border-left:0}
}
@media (max-width:900px){
  /* Stacked there is no clean left half to hide the copy in, so the scrim goes
     vertical and the plate swaps to the tall crop at 700. */
  /* Not min-height:0 - the section collapsed to 387px at 768 and the mosaic
     showed as a dark sliver, which is the whole point gone. Give it room and
     swap to the tall plate here rather than at 700. */
  .gp-hero-mosaic{min-height:clamp(430px,56vw,560px)}
  /* The eyebrow measured 4.74:1 at 900 against the plate - passing, but inside
     the noise of webp compression. A little more scrim buys real margin. */
  .gp-hero-mosaic::after{
    background:linear-gradient(180deg,rgba(7,9,11,.92) 0%,rgba(7,9,11,.79) 38%,rgba(7,9,11,.94) 100%)}
  .gp-hero-mosaic-in h1,.gp-hero-mosaic-in .gp-body-lg{max-width:none}
  .gp-svc-layout{grid-template-columns:1fr;gap:var(--gp-space-10)}
  .gp-svc-index{position:static;flex-direction:row;flex-wrap:wrap;gap:var(--gp-space-2);
    padding-bottom:var(--gp-space-5);border-bottom:1px solid var(--gp-border-hairline)}
  /* the index pills measured 36px tall on a touch viewport, under the 44 min */
  .gp-svc-index a:not(.gp-btn){border-left:0;border:1px solid var(--gp-border-hairline);
    border-radius:var(--gp-radius-pill);padding:10px 14px;min-height:44px;align-items:center}
  .gp-svc-index a.is-active:not(.gp-btn){border-color:var(--gp-action-primary)}
  .gp-svc-index .gp-btn{width:100%;margin-top:var(--gp-space-2)}
  .gp-contact-hero,.gp-reviews-hero,.gp-value,.gp-theme,.gp-two-up{grid-template-columns:1fr}
  .gp-story-cols{grid-template-columns:1fr;gap:var(--gp-space-8)}
  .gp-story-cols > .gp-photo{height:clamp(240px,48vw,340px)}
  .gp-story-quote{grid-template-columns:1fr;align-items:start}
  .gp-contact-card{position:static}
  .gp-stats-strip{grid-template-columns:repeat(2,minmax(0,1fr))}
  .gp-spec-table .row{grid-template-columns:1fr;gap:var(--gp-space-2)}
  .gp-numbers,.gp-process{grid-template-columns:1fr}
  .gp-numbers > div,.gp-process > div{border-left:0;border-top:1px solid var(--gp-border-hairline)}
  .gp-numbers > div:first-child,.gp-process > div:first-child{border-top:0}
  .gp-why-head{max-width:none}
  /* Still space-between, not start: left-packing three content-sized columns
     left the row covering 346px of 744 and put the empty half straight back. */
  .gp-marque-row{grid-template-columns:repeat(3,auto);gap:var(--gp-space-4) var(--gp-space-6)}
  .gp-why-body{grid-template-columns:1fr;gap:var(--gp-space-5)}
  /* Stacked from here down the photo goes full width, and the flat 200px this
     used to be turned a 3:4 phone frame into a 4:1 slit on a tablet. Scale it
     with the viewport instead; the floor keeps it from vanishing on a phone. */
  .gp-entry .gp-photo{height:clamp(200px,40vw,320px)}
  /* Stacked there is no right column to bleed off, so the photo covers the
     whole hero and only the top and bottom edges are masked. */
  .gp-hero-edge{width:100%;
    -webkit-mask-image:linear-gradient(90deg,#000,#000),
      linear-gradient(180deg,transparent 0%,#000 26%,#000 68%,transparent 100%);
    mask-image:linear-gradient(90deg,#000,#000),
      linear-gradient(180deg,transparent 0%,#000 26%,#000 68%,transparent 100%)}
  .gp-hero-edge img{object-position:56% 60%;filter:saturate(.85) brightness(.66)}
  /* The eyebrow sat at 4.72:1 over the photo at 900px - passing, but inside
     the noise of image compression. A little more scrim buys real margin. */
  .gp-hero-edge-sec::after{
    background:linear-gradient(180deg,rgba(7,9,11,.88) 0%,rgba(7,9,11,.72) 38%,rgba(7,9,11,.92) 100%)}
  .gp-hero-edge-in .gp-stack{max-width:none}
}
@media (max-width:600px){
  .gp-marque-row{grid-template-columns:repeat(2,auto)}
  .gp-masonry{column-count:1}
  .gp-stats-strip{grid-template-columns:1fr}
}

/* Vertically centring the score card against a four-element column left it
   floating with nothing to align to. Hang both columns off the same top. */
.gp-reviews-hero{align-items:start}

/* ============================================================
   Home — services accordion
   Replaces the 12-card grid. Native <details>: no JavaScript, and
   every description stays in the HTML whether a row is open or
   not, so crawlers get the copy either way. Closed, the section
   is ~960px on a phone instead of ~2770px.
   ============================================================ */
.gp-svc-list{border-top:1px solid var(--gp-border-hairline)}
.gp-svc-item{border-bottom:1px solid var(--gp-border-hairline)}
.gp-svc-item > summary{
  display:grid;grid-template-columns:38px 24px minmax(0,1fr) auto 18px;
  align-items:center;gap:var(--gp-space-4);
  padding:var(--gp-space-4) var(--gp-space-3);
  min-height:var(--gp-tap-min);cursor:pointer;list-style:none;
  transition:background-color var(--gp-dur-fast) var(--gp-ease-standard)}
/* both spellings are needed — WebKit uses the pseudo-element, Firefox ::marker */
.gp-svc-item > summary::-webkit-details-marker{display:none}
.gp-svc-item > summary::marker{content:""}
.gp-svc-item > summary:hover{background:var(--gp-surface-card)}
/* the whole open item shares one surface, so it reads as a single block
   rather than a lit header sitting on top of a dark panel */
.gp-svc-item[open]{background:var(--gp-surface-card)}
.gp-svc-item .n{font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);color:var(--gp-text-accent)}
.gp-svc-item > summary .gp-icon{color:var(--gp-blue-400)}
.gp-svc-item h3{font:var(--gp-heading-2);color:var(--gp-text-strong)}
.gp-svc-item .tag{font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);
  text-transform:uppercase;color:var(--gp-text-muted);white-space:nowrap;text-align:right}

/* plus that becomes a minus. Drawn rather than iconised so the two
   states share a stroke and the transition has something to animate. */
.gp-svc-mark{position:relative;width:14px;height:14px;justify-self:end}
.gp-svc-mark::before,.gp-svc-mark::after{content:"";position:absolute;background:var(--gp-steel-300);
  transition:transform var(--gp-dur-base) var(--gp-ease-standard),
             opacity var(--gp-dur-base) var(--gp-ease-standard),
             background-color var(--gp-dur-fast) var(--gp-ease-standard)}
.gp-svc-mark::before{inset:6px 0;height:2px}
.gp-svc-mark::after{inset:0 6px;width:2px}
.gp-svc-item[open] .gp-svc-mark::before{background:var(--gp-blue-400)}
.gp-svc-item[open] .gp-svc-mark::after{transform:rotate(90deg);opacity:0}

/* panel copy hangs off the same left edge as the title */
.gp-svc-panel{display:flex;flex-wrap:wrap;align-items:center;gap:var(--gp-space-4) var(--gp-space-6);
  padding:0 var(--gp-space-3) var(--gp-space-5)
          calc(38px + 24px + (var(--gp-space-4) * 2) + var(--gp-space-3))}
.gp-svc-panel p{font:var(--gp-body-sm);color:var(--gp-text-body);max-width:70ch;margin:0}

/* ---------------- FAQ ----------------
   Reuses the accordion's chrome but not its column grid: that grid is
   38px 24px 1fr auto 18px, sized for the home page's number, icon, title and
   tag. A question has only a title and a mark, so on the five-track grid the
   h3 landed in the 38px column and every summary stacked on top of the next.
   Two tracks here, and the panel hangs off the same left edge as the title. */
.gp-faq .gp-svc-item > summary{grid-template-columns:minmax(0,1fr) 18px;
  gap:var(--gp-space-5);padding:var(--gp-space-5) var(--gp-space-3);align-items:start}
.gp-faq .gp-svc-item h3{font:var(--gp-heading-2);color:var(--gp-text-strong);
  text-wrap:balance}
.gp-faq .gp-svc-mark{margin-top:4px}
.gp-faq .gp-svc-panel{display:block;
  padding:0 var(--gp-space-3) var(--gp-space-5) var(--gp-space-3)}
.gp-faq .gp-svc-panel p{font:var(--gp-body);max-width:78ch}
.gp-svc-panel a{display:inline-flex;align-items:center;gap:7px;min-height:var(--gp-tap-min);
  font:var(--gp-label);letter-spacing:var(--gp-track-caps);text-transform:uppercase;
  color:var(--gp-blue-400);white-space:nowrap}
.gp-svc-panel a:hover{color:var(--gp-white)}

.gp-svc-foot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:var(--gp-space-5);margin-top:var(--gp-space-8)}
.gp-svc-foot > div{max-width:62ch;display:flex;flex-direction:column;gap:var(--gp-space-2)}

@media (max-width:900px){
  .gp-svc-item > summary{grid-template-columns:32px minmax(0,1fr) 18px;gap:var(--gp-space-3)}
  .gp-svc-item > summary > .gp-icon{display:none}
  .gp-svc-item .tag{display:none}
  .gp-svc-panel{padding-left:calc(32px + (var(--gp-space-3) * 2))}
}

/* ============================================================
   Home — review marquee
   Continuous side-scroll of verbatim Google reviews. CSS only:
   the track holds the card set twice and slides exactly half its
   width, so the seam never shows.
   ============================================================ */
.gp-marquee{--gp-marquee-gap:20px;--gp-marquee-card:340px;
  position:relative;overflow:hidden;padding-block:var(--gp-space-2)}
/* Fade the cards out at both edges instead of guillotining them. */
@supports (mask-image:linear-gradient(90deg,#000,#000)) or (-webkit-mask-image:linear-gradient(90deg,#000,#000)){
  .gp-marquee{
    -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 7%,#000 93%,transparent 100%);
            mask-image:linear-gradient(90deg,transparent 0,#000 7%,#000 93%,transparent 100%)}
}
.gp-marquee-track{display:flex;gap:var(--gp-marquee-gap);width:max-content;
  align-items:stretch;will-change:transform;
  animation:gp-marquee-slide 64s linear infinite}
.gp-marquee-track > .gp-review{flex:0 0 var(--gp-marquee-card);margin:0}
.gp-marquee-track .gp-hairline{margin-top:auto}   /* pin the name to the card foot */

/* Half the track is exactly one card set — plus one gap, which has to come
   off or every loop drifts by 20px. */
@keyframes gp-marquee-slide{
  from{transform:translate3d(0,0,0)}
  to{transform:translate3d(calc(-50% - (var(--gp-marquee-gap) / 2)),0,0)}
}

/* Pause: hover, keyboard focus inside the strip, or the explicit control. */
.gp-marquee:hover .gp-marquee-track,
.gp-marquee:focus-within .gp-marquee-track,
.gp-marquee-toggle:checked ~ .gp-marquee .gp-marquee-track{animation-play-state:paused}

/* The control is a checkbox so it works with no JavaScript. WCAG 2.2.2 wants
   a pause mechanism for anything that moves by itself for more than five
   seconds, and hover-to-pause does nothing for a keyboard or touch user. */
.gp-marquee-toggle{position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}
.gp-marquee-bar{display:flex;justify-content:flex-end;margin-top:var(--gp-space-4)}
.gp-marquee-btn{display:inline-flex;align-items:center;gap:9px;cursor:pointer;
  min-height:var(--gp-tap-min);padding:0 var(--gp-space-4);
  border:1px solid var(--gp-border-hairline);border-radius:var(--gp-radius-pill);
  font:var(--gp-mono-sm);letter-spacing:var(--gp-track-caps);text-transform:uppercase;
  color:var(--gp-text-muted);transition:var(--gp-transition-control)}
.gp-marquee-btn:hover{color:var(--gp-text-strong);border-color:var(--gp-border-strong)}
.gp-marquee-toggle:focus-visible + .gp-marquee + .gp-marquee-bar .gp-marquee-btn{
  outline:2px solid var(--gp-focus-ring);outline-offset:2px}
/* Two bars while it runs, one triangle when it is paused, so the control
   always states what it will do next. A single clip-path cannot describe two
   disjoint rectangles, so the bars are pseudo-elements. */
.gp-marquee-ico{position:relative;width:10px;height:12px}
.gp-marquee-ico::before,.gp-marquee-ico::after{content:"";position:absolute;
  top:0;bottom:0;width:3px;background:currentColor}
.gp-marquee-ico::before{left:0}
.gp-marquee-ico::after{right:0}
.gp-marquee-toggle:checked ~ .gp-marquee-bar .gp-marquee-ico::after{display:none}
.gp-marquee-toggle:checked ~ .gp-marquee-bar .gp-marquee-ico::before{
  inset:0 auto auto 0;width:0;height:0;background:none;
  border-left:10px solid currentColor;
  border-top:6px solid transparent;border-bottom:6px solid transparent}
.gp-marquee-toggle:checked ~ .gp-marquee-bar .on-play,
.gp-marquee-bar .on-pause{display:none}
.gp-marquee-toggle:checked ~ .gp-marquee-bar .on-pause{display:inline}

@media (max-width:600px){ .gp-marquee{--gp-marquee-card:278px} }

/* Reduced motion: stop it outright and hand back a scroll container, so the
   reviews are still reachable. The global reduce rule only shortens
   animation-duration, which on an infinite loop would spin it into a blur. */
@media (prefers-reduced-motion:reduce){
  .gp-marquee{overflow-x:auto;scroll-snap-type:x mandatory;
    -webkit-mask-image:none;mask-image:none}
  .gp-marquee-track{animation:none !important;will-change:auto}
  .gp-marquee-track > .gp-review{scroll-snap-align:start}
  .gp-marquee-track > .gp-review[aria-hidden="true"]{display:none}
  .gp-marquee-bar{display:none}
}

/* ============================================================
   Home — "What makes Kevin different"
   The lead argument carries a photograph; the four supporting
   points stack beside it. Replaces a bordered 5-cell grid that
   gave every point equal weight and left the page with two
   screens of unbroken text between the hero and the photo strip.
   ============================================================ */
.gp-why{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:var(--gp-space-12);align-items:stretch}
.gp-why-lead{display:flex;flex-direction:column;gap:var(--gp-space-4);align-items:flex-start}
/* The source is portrait (1000x1333). A landscape crop throws away the open
   hood and the branded plate, which are the whole point of the shot — so the
   frame is tall and the crop favours the car over the sky. */
.gp-why-lead .gp-photo{width:100%;height:390px}
.gp-why-lead .gp-photo img{object-position:center 52%}
/* The standard protect gradient exists to keep a caption readable over a
   small tile. At this size it swallowed the grille and the branded plate,
   so this one fades far later and only as far as it has to. */
.gp-why-lead .gp-photo-protect::after{
  background:linear-gradient(180deg,rgba(7,9,11,0) 0%,rgba(7,9,11,.06) 58%,rgba(7,9,11,.82) 100%)}
.gp-why-idx{font:var(--gp-mono-md);letter-spacing:var(--gp-track-caps);
  color:var(--gp-text-accent);margin-top:var(--gp-space-2)}
.gp-why-lead p{color:var(--gp-text-body)}

.gp-why-list{display:flex;flex-direction:column;justify-content:space-between}
.gp-why-item{display:grid;grid-template-columns:26px minmax(0,1fr);gap:var(--gp-space-4);
  padding:var(--gp-space-6) 0;border-top:1px solid var(--gp-border-hairline)}
.gp-why-item:first-child{border-top:0;padding-top:0}
.gp-why-item:last-child{padding-bottom:0}
.gp-why-item > .gp-icon{color:var(--gp-blue-400);margin-top:3px}
.gp-why-item h3{margin-bottom:6px}

@media (max-width:900px){
  .gp-why{grid-template-columns:1fr;gap:var(--gp-space-10)}
  .gp-why-lead .gp-photo{height:340px}
  .gp-why-item{padding:var(--gp-space-5) 0}
}

/* Reviews — the marques behind the reviews.
   Portrait sources in a 190px-tall strip means a thin horizontal band out of
   each, so every frame needs its own crop or the cars fall out of shot. */
.gp-review-cars{margin-top:var(--gp-space-12)}
.gp-review-cars .gp-eyebrow{margin-bottom:var(--gp-space-4)}
/* A 190px band was catching open bonnets rather than cars; taller frames and
   lower crops put the bodywork and the badge in shot. */
.gp-review-cars .gp-photo-strip .gp-photo{height:250px}
.gp-review-cars img[src*="audi-tts"]{object-position:center 74%}
.gp-review-cars img[src*="mercedes-g-class"]{object-position:center 70%}
.gp-review-cars img[src*="bmw-4-series"]{object-position:center 76%}
.gp-review-cars img[src*="porsche-cayenne-driveway"]{object-position:46% 74%}

/* The header condenses only on desktop, so a phone has to clear the full
   two-row bar on an anchor jump — 124px left a deep-linked heading 1px
   underneath it. */
@media (max-width:900px){
  /* The header was 150-166px tall as a two-row bar; with the menu button it is
     92-98, so 156 now overshoots and drops the heading well below the header. */
  .gp-entry{scroll-margin-top:112px}
  .gp-entry-main{grid-template-columns:1fr}
}

/* Contact — location map.
   Third-party iframe, so: lazy (it sits well below the fold), a title for
   screen readers, and a referrer policy. Framed like a photo rather than
   colour-filtered — a tinted map is a harder map to read, and legibility
   beats matching the palette here. */
.gp-map{margin-top:var(--gp-space-10)}
.gp-map iframe{display:block;width:100%;aspect-ratio:21/9;min-height:260px;
  border:1px solid var(--gp-border-hairline);border-radius:var(--gp-radius-card);
  box-shadow:var(--gp-shadow-2);background:var(--gp-surface-card)}
.gp-map figcaption{margin-top:var(--gp-space-3);color:var(--gp-text-muted)}
@media (max-width:760px){ .gp-map iframe{aspect-ratio:4/3} }

/* A closed service row is pinned to 56px so the blueprint grid behind it
   divides the row exactly twice. Desktop only — below 900px the longer
   titles wrap to two lines and a fixed height would clip them.
   This lives in pages.css rather than site.css because the base rule is
   here, and the bundle puts this file last. */
@media (min-width:901px){
  .gp-svc-item > summary{height:55px;padding-block:13px}
}

/* An open row has to land on the grid too. Closed it is 56px (2 x 28), but
   open it was 56 + a 64px panel = 120, and 120 mod 28 is 8 — so every row
   below an open one sat 8px out of phase, and the first row is open on load.
   Trimming the panel's foot from 20px to 12px makes it 56, so an open row is
   112px, exactly four cells. */
@media (min-width:901px){
  .gp-svc-panel{padding-bottom:var(--gp-space-3)}
}