/* Laptop Clinic — design system v2
   Language: Apple (typography-led, restrained, glow/dark hero, pill buttons,
   generous whitespace) merged with Microsoft (rounded tile cards, soft
   shadows, colour-accented icon badges). Brand accent stays red. */

:root {
  --black: #000000;
  --near-black: #0a0a0c;
  --text: #1d1d1f;
  --text-soft: #48484a;
  --gray-500: #6e6e73;
  --gray-400: #a1a1a6;
  --gray-300: #d2d2d7;
  --gray-200: #e5e5ea;
  --gray-100: #eaeaec;
  --gray-50: #f5f5f7;
  --white: #ffffff;

  --red: #e63946;
  --red-dark: #c8202d;
  --red-tint: rgba(230, 57, 70, 0.1);
  /* Height of the fixed phone call bar (<=620px), before the home-indicator inset. The
     body padding, the menu sheet and the scroll padding all clear it from this one value. */
  --callbar-h: 64px;
  --blue: #0068c9;
  --blue-tint: rgba(0, 104, 201, 0.1);
  --navy: #0b1220;
  --navy-soft: #121a2b;
  --paper: #fbfaf9;
  /* Homepage chapter palette. --near-black (#0a0a0c) is the primary dark;
     these two complete it. Kept separate from --paper so the secondary
     pages that use .section-alt are not dragged along. */
  --dark-surface: #141416;
  --surface-light: #f5f5f7;
  /* Rating gold. Warm rather than bright so it reads as a mark of quality
     next to the red accent instead of competing with it. */
  --gold: #f5b301;
  /* Same red, one step deeper: the hover/pressed state of buttons that already sit on
     --red-dark. White text on --red is 4.17:1 (fails AA for button text), on --red-dark 5.68:1,
     so --red-dark carries text and button fills; --red stays for icons, rules and rings. */
  --red-deep: #a81b26;
  /* Field borders: --gray-300 was 1.51:1 against white, under the 3:1 WCAG 1.4.11 needs to
     show where a field is. */
  --gray-450: #86868b;
  /* Outcome colours for status messages (never used as accents). */
  --success: #1e6b3c;
  --success-tint: #e6f6ec;
  --success-line: #b3e3c4;
  --error: #a12f2f;
  --error-tint: #fdecec;
  --error-line: #f3b8b8;
  /* Type and spacing steps for new work. Small text never goes below --fs-xs (12px). */
  --fs-xs: 0.75rem;
  --fs-sm: 0.875rem;
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-5: 24px; --space-6: 32px; --space-7: 48px; --space-8: 64px;

  --shadow-sm: 0 1px 2px rgba(11, 18, 32, 0.07), 0 1px 1px rgba(11, 18, 32, 0.05);
  --shadow-md: 0 12px 28px rgba(11, 18, 32, 0.12);
  --shadow-lg: 0 24px 60px rgba(11, 18, 32, 0.18);
  --ring-red: 0 0 0 1px rgba(230, 57, 70, 0.35);

  --radius-sm: 12px;
  --radius: 18px;
  --radius-lg: 28px;
  --pill: 980px;

  --ease-out: cubic-bezier(.16,1,.3,1);
  --ease-io: cubic-bezier(.65,0,.35,1);
  --dur-fast: 120ms;
  --dur-base: 220ms;
  --dur-slow: 420ms;

  --max-width: 1180px;
  --font: "Manrope", -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
    "Segoe UI Variable Display", "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }
/* scroll-padding-top keeps anchor jumps and keyboard focus clear of the sticky header
   (73-81px tall); WCAG 2.4.11. Headings need no scroll-margin of their own on top of it. */
html { scroll-behavior: smooth; scroll-padding-top: 96px; }
/* iOS enlarges text on rotation unless told not to; the type is already sized for phones. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
/* The header is sticky and always on screen; without this, focusing one of its controls
   (Shift+Tab, Escape from the menu) scrolled the page ~400px to "clear" the header. */
.site-header a, .site-header button { scroll-margin-top: -96px; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

body {
  margin: 0;
  font-family: var(--font);
  color: var(--text);
  background: var(--white);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  font-size: 18px;
}

img, svg { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }

.container {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 24px;
}
.container-wide { max-width: 1320px; margin: 0 auto; padding: 0 24px; }
/* The pages use viewport-fit=cover so the fixed call bar can clear the iPhone home
   indicator; in landscape that also lets the page run under the notch, so the gutter
   grows to the safe area there. The insets are zero everywhere else. */
.container, .container-wide {
  padding-left: max(24px, env(safe-area-inset-left));
  padding-right: max(24px, env(safe-area-inset-right));
}

h1, h2, h3, h4 {
  line-height: 1.08;
  margin: 0 0 0.4em;
  color: var(--text);
  font-weight: 600;
  letter-spacing: -0.015em;
}
h1 { font-size: clamp(2.75rem, 7vw, 6.75rem); letter-spacing: -0.045em; }
h2 { font-size: clamp(2rem, 4vw, 2.9rem); letter-spacing: -0.02em; }
h3 { font-size: 1.2rem; letter-spacing: -0.01em; }
p { margin: 0 0 1em; color: var(--text-soft); }
.lead { font-size: clamp(1.1rem, 2vw, 1.4rem); color: var(--text-soft); font-weight: 400; }

.icon { width: 1em; height: 1em; stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }

/* Words for assistive technology only, such as "(opens in a new tab)" after a link. */
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
/* Keeps a pair that reads as one unit, such as "Sat 9am–2pm", on one line. */
.nowrap { white-space: nowrap; }
@media (max-width: 30em) { .visit-info-value .nowrap { white-space: normal; } }
h1, h2, h3 { overflow-wrap: break-word; }

/* Scroll reveal */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.7s ease, transform 0.7s ease; }
.reveal.in-view { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; } }

/* The grid stagger rules that used to live here are gone deliberately.
   Every tile fading up a beat after the last was the single most repeated
   piece of motion on the site (96 elements) and reads as generated rather
   than designed. The site now has one orchestrated moment - the hero
   scroll-scrub on index.html - and nothing competes with it.
   .reveal above is kept as an available mechanism, currently unused: if a
   future page needs one deliberate entrance, mark that ONE element. Do not
   reapply it per-card or per-section. */

/* Hero laptop reveal video — sleek product-shot loop, screen lighting up */

/* Buttons — pill-first (Apple), colour from brand */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 28px;
  border-radius: var(--pill);
  font-family: inherit; /* <button> otherwise falls back to the UA font (Arial) */
  font-weight: 500;
  font-size: 1rem;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
  white-space: nowrap;
}
.btn:active { transform: scale(0.97); }
/* These have pressed states of their own; the browser's grey tap rectangle ignored their
   rounded corners and flashed over them. */
.btn, .service-card, .service-tile, .service-jump a, .brand-major-card { -webkit-tap-highlight-color: transparent; }
/* --red-dark, not --red: white on --red is 4.17:1, under AA for button text (5.68:1 here). */
.btn-primary { background: var(--red-dark); color: var(--white); }
.btn-secondary { background: transparent; color: var(--text); border-color: var(--gray-300); }
.btn-glass { background: rgba(255,255,255,0.12); color: var(--white); border-color: rgba(255,255,255,0.35); backdrop-filter: blur(12px); }
/* Hover only where a hover exists, so a tap on a phone doesn't leave a button stuck lifted. */
@media (any-hover: hover) {
  .btn:hover { transform: translateY(-1px); }
  .btn-primary:hover { background: var(--red-deep); box-shadow: var(--shadow-md); }
  .btn-secondary:hover { border-color: var(--text); }
  .btn-glass:hover { background: rgba(255,255,255,0.2); }
}
/* Busy (the contact form while sending): aria-disabled keeps keyboard focus on the button. */
.btn[aria-disabled="true"], .btn:disabled { opacity: 0.65; cursor: progress; transform: none; box-shadow: none; }
/* One focus ring for every link and control, red on light surfaces and white on dark ones.
   :where() keeps it at zero specificity, so component rules (tiles, inputs, hero) still win. */
:where(a, button, [tabindex], summary):focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }
:where(.topbar, .cta-bubble, .mobile-call-bar, .page-hero, .hero-glow, .services-unlisted, .hero-video-toggle) :where(a, button, [tabindex]):focus-visible,
.hero-video-toggle:focus-visible { outline-color: var(--white); }
/* The sticky header's frosted glass composites to ~200 grey over the dark heroes, where the
   --red ring is 2.5:1; --red-dark is 3.4:1 there and 5.7:1 on white. */
.site-header :where(a, button, [tabindex]):focus-visible { outline-color: var(--red-dark); }
.btn-block { width: 100%; }
.btn .icon { width: 18px; height: 18px; }

/* Apple-style "Learn more >" link */
.link-arrow {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--blue); font-weight: 500; font-size: 1rem;
  transition: gap 0.15s ease;
}
.link-arrow .icon { width: 15px; height: 15px; transition: transform 0.15s ease; }
@media (any-hover: hover) { .link-arrow:hover .icon { transform: translateX(3px); } }
.link-arrow.on-dark { color: #6cb2ff; }

/* Top info bar */
.topbar { background: var(--near-black); color: var(--gray-300); font-size: 0.8rem; }
.topbar .container {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 8px; padding-top: 8px; padding-bottom: 8px;
}
.topbar-links { display: flex; gap: 20px; flex-wrap: wrap; }
.topbar-links a { display: inline-flex; align-items: center; gap: 6px; }
@media (any-hover: hover) { .topbar-links a:hover { color: var(--white); } }
.topbar .icon { width: 14px; height: 14px; }
/* Phones, small tablets and landscape phones: one row of the two tappable numbers (the
   landline stays, per the owner). The address and email group goes - both are in the
   footer, on Contact and in the closing bands' "Or drop in" line - so the chrome above the
   hero is one short row, not two or three, and "Get a Free Quote" clears the Call bar on a
   360x640 phone. Up to 740px the group would wrap the bar to two rows; on a landscape
   phone it took 65px of a 375px screen. The numbers become 36px targets in the same bar. */
@media (max-width: 46.25em), (max-width: 899px) and (max-height: 500px) {
  .topbar-links { row-gap: 4px; }
  .topbar .topbar-links:first-child { display: none; }
  .topbar .container { justify-content: center; padding-top: 0; padding-bottom: 0; }
  .topbar-links a { min-height: 36px; }
}

/* Nav — blurred glass bar */
.site-header {
  position: sticky; top: 0; z-index: 100;
  border-bottom: 1px solid transparent;
}
/* Glass on a pseudo-element, not the header: backdrop-filter would make the header the
   containing block for the fixed mobile drawer, trapping it in the bar and pushing the
   closed drawer past the right edge. The hairline comes along because a z-index:-1 child
   paints over its parent's border; the header's transparent border keeps its height. */
.site-header::before {
  content: ""; position: absolute; inset: 0 0 -1px 0; z-index: -1;
  background: rgba(255, 255, 255, 0.78);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid rgba(0,0,0,0.06);
}
/* padding-block, not the `padding: 12px 0` shorthand: the row is also a .container, and the
   shorthand zeroed its 24px side gutter, so below 1180px the header ran edge to edge and
   everywhere it sat 24px wider than the page content beneath it. */
.nav-row { display: flex; align-items: center; justify-content: space-between; padding-block: 12px; gap: 16px; }
/* nowrap + a fixed icon: with no room to spare, flex used to break "Laptop / Clinic" and
   squash the pulse icon to 20px rather than let anything else give way. */
.logo { display: flex; align-items: center; gap: 9px; font-weight: 600; font-size: 1.15rem; color: var(--text); letter-spacing: -0.01em; white-space: nowrap; }
.logo .icon { width: 26px; height: 26px; color: var(--red); stroke-width: 2; flex-shrink: 0; }
.logo small { display: block; font-size: var(--fs-xs); font-weight: 500; letter-spacing: 0.07em; color: var(--text-soft); text-transform: uppercase; }

.main-nav { display: flex; align-items: center; gap: 30px; }
.main-nav a { font-weight: 500; font-size: 0.92rem; color: var(--text-soft); padding: 8px 0; border-bottom: 2px solid transparent; }
.main-nav a[aria-current="page"] { color: var(--text); border-color: var(--red); }
@media (any-hover: hover) { .main-nav a:hover { color: var(--text); border-color: var(--red); } }
.nav-cta { display: flex; align-items: center; gap: 14px; flex-shrink: 0; }
.nav-toggle { display: none; background: none; border: none; cursor: pointer; padding: 6px; }
.nav-toggle .icon { width: 24px; height: 24px; color: var(--text); }

@media (max-width: 53.75em) {
  /* The menu is a sheet that opens under the header, full width, rather than a drawer
     sliding over it from the right: the drawer clipped the logo to "Lap", hid the Call
     circle, and moved its X relative to the links as the header changed height. The
     header stays whole above the sheet; js/script.js sets --drawer-top to the header's
     bottom edge when it opens (the page can't scroll while it is open, so it holds). */
  .main-nav {
    /* z-index: above .nav-backdrop (90), or the dimmer covers the drawer and eats its taps */
    position: fixed; inset: var(--drawer-top, 73px) 0 0 0; z-index: 91;
    background: var(--white);
    flex-direction: column; align-items: stretch;
    padding: 8px max(24px, env(safe-area-inset-right)) 24px max(24px, env(safe-area-inset-left));
    gap: 0;
    /* Short screens: the links scroll inside the sheet rather than run off the bottom of
       a page that is locked. */
    overflow-y: auto; overscroll-behavior: contain;
    /* Hidden once it has faded out, so its links don't take keyboard focus. */
    opacity: 0; transform: translateY(-8px); visibility: hidden;
    transition: opacity 0.2s ease, transform 0.25s var(--ease-out), visibility 0s linear 0.25s;
  }
  .main-nav.open { opacity: 1; transform: none; visibility: visible; transition-delay: 0s; }
  /* Full-width rows: the whole row is the target, not just the word. */
  .main-nav a { font-size: 1.375rem; font-weight: 600; color: var(--text); padding: 14px 0; border-bottom: 1px solid var(--gray-100); }
  .main-nav a[aria-current="page"] { color: var(--red-dark); border-bottom-color: var(--gray-100); }
  .nav-toggle { display: inline-flex; }
  /* Hidden visually but still read out, as .visually-hidden does: display: none here left
     the icon-only Call link with no accessible name. Absolutely positioned, it takes no
     space in the flex row. */
  .nav-cta .btn-text {
    position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  /* With its label hidden, Call is a 48px circle, not a 76px pill still padded for the
     words; the menu button gets a 44px tap target (it was 36px). The negative margin puts
     the glyph, not the padded box, on the gutter, in line with the topbar number above. */
  .nav-cta .btn { width: 48px; height: 48px; padding: 0; }
  .nav-toggle { padding: 10px; margin-right: -10px; }
  /* Starts where the sheet does, so it never dims the header (the logo, Call and the X). */
  .nav-backdrop { position: fixed; inset: var(--drawer-top, 73px) 0 0 0; background: rgba(0,0,0,0.4); z-index: 90; opacity: 0; pointer-events: none; transition: opacity 0.3s cubic-bezier(.4,0,.2,1); }
  .nav-backdrop.open { opacity: 1; pointer-events: auto; }
  /* Above the open drawer (91, same stacking context), so it stays visible as the close button. */
  .nav-toggle { position: relative; z-index: 92; }
  html.nav-open { overflow: hidden; }
}
/* Landscape phones: six 60px rows don't fit under the header, so they go two across. */
@media (max-width: 53.75em) and (max-height: 500px) {
  .main-nav { display: grid; grid-template-columns: 1fr 1fr; column-gap: 32px; align-content: start; }
}
/* Phones: the call bar stays above the open menu (Call and Enquire belong in a menu), so
   the sheet keeps its last link clear of it. */
@media (max-width: 620px) {
  .main-nav { padding-bottom: calc(24px + var(--callbar-h) + env(safe-area-inset-bottom)); }
  html.nav-open .mobile-call-bar { z-index: 101; }
}
/* Above 860px the drawer's empty .nav-backdrop is unstyled but still a flex item in the
   header row: it added a third 16px gap, left the menu twice as far from the logo as from
   Call Now, and pushed Call Now 3px into the gutter at 861-864px. Out of the row, the menu
   sits evenly between the two. */
@media (min-width: 53.8125em) {
  .nav-backdrop { display: none; }
}
/* The breakpoints below are in em, not px: the logo is sized in rem and never wraps, so
   they have to follow the browser's text-size setting (at "very large", 24px, a px
   breakpoint left the 320px row 19px too wide). At the default 16px, 1em = 16px. */
/* The tagline shows only where it fits beside the name. Since it became "Oxley QLD"
   (2026-10-30) the row needs ~520px with the drawer menu, and with the full menu it leaves
   under 50px between the logo and "Home" below ~1140px. Rather than wrap both lines of the
   logo or crowd the menu, the tagline steps aside there; the hero eyebrow directly below
   already says "Repair Specialists". */
@media (max-width: 33.75em), (min-width: 53.8125em) and (max-width: 71.1875em) {
  .site-header .logo small { display: none; }
}
/* Phones: the header and the call bar are on screen all the time (149px of a 568px
   screen), so the header gives back 8px: a 44px Call circle, still a full target. */
@media (max-width: 620px) {
  .nav-row { padding-block: 10px; }
  .nav-cta .btn { width: 44px; height: 44px; }
}
/* 320px phones (22.5em = 360px): tighter gaps keep 13px to spare instead of 3. */
@media (max-width: 22.5em) {
  .nav-row { gap: 12px; }
  .nav-cta { gap: 8px; }
}
/* Below ~310px (19.3125em = 309px; folded-phone cover screens) even the name alone does
   not fit beside both buttons. The fixed Call bar at the bottom already dials that number. */
@media (max-width: 19.3125em) {
  .site-header .nav-cta .btn { display: none; }
}

/* Hero — full-bleed glow, Apple keynote style.
   This used to pin: .hero-glow was a tall spacer (JS set it to 100vh plus a
   scroll runway) and .hero-scrub-sticky stuck to the top of the viewport
   while that runway scrolled past, to buy the frame-scrub a scroll distance
   of its own. The video plays on its own clock now, so none of that is
   needed - the hero is an ordinary block one viewport tall and the page
   scrolls at its normal rate straight past it.
   The class name is kept as-is: it is referenced in one place on one page,
   and renaming it buys nothing worth the churn.
   min-height (not height) so the copy can never be clipped on a short
   viewport; svh accounts for mobile browser chrome where supported. */
.hero-glow {
  position: relative;
}
.hero-scrub-sticky {
  position: relative; min-height: 100vh; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  /* Flat, and deliberately so. This used to ramp navy -> #143363 -> blue ->
     white, which both introduced a second dominant background colour and
     resolved the hero into white immediately before a dark object. The
     video covers this almost everywhere; what shows is the mobile
     letterbox area, which should simply be the dark chapter colour. */
  background: var(--near-black);
  color: var(--white);
  text-align: center;
}
/* svh tracks the viewport with mobile browser chrome showing, so the hero
   does not get taller than the screen the moment the address bar appears. */
@supports (min-height: 100svh) {
  .hero-scrub-sticky { min-height: 100svh; }
}

/* Hero background video — native <video>, playing on its own clock.
   Nothing is applied to the moving layer itself beyond object-fit and the
   tiny scroll transform: no filter and no mask. Both were inherited from the
   frame sequence, where they cost one composite on a static <img>; on video
   they would be re-applied to every decoded frame for the life of the loop.
   All of the contrast and the seam into the section below now come from the
   static .hero-video-scrim sitting on top, which is painted once.
   object-fit: cover crops rather than stretches, so the laptop keeps its
   proportions at every viewport. */
.hero-video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  z-index: 0;
  transform-origin: center 55%;
}
/* The poster carries the same grade so the handoff to the first decoded
   frame is invisible - without this the image would pop as playback starts. */
.hero-video[poster] { background: var(--near-black); }
/* Portrait phones and tablets: one stage, not copy above a video box (owner, 2026-10-30).
   The footage is 16:9, so on a portrait screen it is drawn wider than the screen
   (--stage-w) and anchored to the hero's foot: the laptop spans the content width and
   rises under the buttons. The scrim feathers the frame's top edge into the dark above it
   and its foot into the trust rail, so no rectangle shows anywhere; the buttons sit over
   the darkened top of the lid. The hero is content-driven - the copy, then the stage below
   it (padding-bottom is 72% of the frame's height). Landscape phones keep the desktop
   composition, which suits a wide screen. Scoped to .hero-glow, so no .page-hero changes. */
@media (max-width: 600px), (max-width: 860px) and (orientation: portrait) {
  .hero-glow { --stage-w: 150vw; --stage-h: calc(var(--stage-w) * 0.5625); }
  .hero-scrub-sticky {
    min-height: 0;
    align-items: flex-start;
    padding: 16px 0 calc(var(--stage-h) * 0.72);
  }
  .hero-glow .hero-video {
    inset: auto auto 0 50%; width: var(--stage-w); height: var(--stage-h);
    margin-left: calc(var(--stage-w) / -2);
  }
  .hero-glow .hero-video-scrim {
    background: linear-gradient(180deg,
      var(--near-black) 0,
      var(--near-black) calc(100% - var(--stage-h)),
      rgba(10,10,12,0.6) calc(100% - var(--stage-h) * 0.86),
      rgba(10,10,12,0) calc(100% - var(--stage-h) * 0.6),
      rgba(10,10,12,0) calc(100% - var(--stage-h) * 0.16),
      var(--near-black) 100%);
  }
}
@media (min-width: 601px) and (max-width: 860px) and (orientation: portrait) {
  .hero-glow { --stage-w: 120vw; }
}
/* Layered, entirely static. Top layer first:
   1. the seam - the hero resolves into the dark trust rail below, which is
      one continuous dark chapter, not a fade through white;
   2. contrast weighted to the copy side, so the laptop on the right keeps
      its detail instead of the whole frame being flattened;
   3. a little depth under the sticky header. */
.hero-video-scrim {
  position: absolute; inset: 0; z-index: 0;
  background:
    linear-gradient(180deg, rgba(10,10,12,0) 82%, rgba(10,10,12,0.6) 93%, var(--near-black) 100%),
    linear-gradient(90deg, rgba(6,8,14,0.88) 0%, rgba(6,8,14,0.74) 30%, rgba(6,8,14,0.45) 48%, rgba(6,8,14,0.12) 72%, rgba(6,8,14,0.02) 100%),
    linear-gradient(180deg, rgba(6,8,14,0.5) 0%, rgba(6,8,14,0) 24%);
}
/* Reduced motion keeps the image (just static, no scrub/scale) rather than
   hiding it - losing the laptop shot and the logo reveal entirely would be
   "zero" motion accommodation, not the "fewer and gentler" one intended. */

.hero-glow .container { position: relative; z-index: 1; max-width: 820px; }
.hero-glow .hero-grid {
  max-width: 1180px; text-align: left;
  display: grid; grid-template-columns: 1fr; gap: 32px; align-items: center;
}
@media (min-width: 900px) {
  .hero-glow .hero-grid { grid-template-columns: 3fr 2fr; gap: 48px; }
}
.hero-col-left { min-width: 0; }
/* Fill the container: as a flex item of the centring .hero-scrub-sticky the grid shrank
   to its widest content (the lead's 48ch) once the h1 became three short lines, and the
   copy drifted in from the gutter at tablet widths. */
.hero-glow .hero-grid { width: 100%; }
.hero-glow .hero-grid .lead { margin-inline: 0; }
/* "hands-on" and "board-level" break at their hyphens on a 320px screen otherwise. */
.hero-grid .lead .nb { white-space: nowrap; }
.hero-grid .hero-actions { justify-content: flex-start; }
.hero-grid .lead { margin-inline: 0; margin-bottom: 18px; }
/* One trust line in place of the free-quote badge and the Google-review row.
   Same facts, one object asking for attention instead of two. */
.hero-grid .hero-trust {
  margin: 0 0 32px; font-size: 0.92rem; font-weight: 500;
  color: rgba(255,255,255,0.72); font-variant-numeric: tabular-nums;
}
/* Each proof item stays whole, so a narrow screen breaks the line at the
   separators rather than inside "No Fix, No Fee". */
.hero-grid .hero-trust__item { white-space: nowrap; }
/* REPAIR. RECOVER. RESTORE. - one word a line at every width, so the three-part
   lockup never reflows into two uneven lines. The capitals come from CSS, not the
   HTML, so the source and copy/paste stay in sentence case (Chromium still hands the
   capitals to screen readers, which read seven-letter words as words). Capitals
   want less negative tracking and tighter leading than the sentence this replaced,
   and read larger than lowercase at the same size, hence the lower cap. The size
   also follows the window's height (8vh): the hero is one viewport tall with its
   copy centred, so on a short 1280x720 laptop window three lines at the full cap
   pushed the CTAs 20px below the fold. Tall screens still get the full 4.6rem.
   (.hero-glow h1 below sets tracking too; this selector outranks it.) */
.hero-glow .hero-grid h1 {
  font-size: clamp(2.6rem, min(6vw, 8vh), 4.6rem); line-height: 0.98;
  text-transform: uppercase; letter-spacing: -0.02em;
}
.hero-glow .hero-grid h1 > span { display: block; }
/* Phones: a step smaller and tighter, so the CTAs clear the fixed Call bar in an iPhone
   Safari window (visible area about 390x664) - three lines of capitals plus a four-line
   lead had pushed "Get a Free Quote" behind it. */
@media (max-width: 600px) {
  .hero-glow .hero-scrub-sticky { padding-top: 16px; }
  .hero-glow .hero-grid h1 { font-size: clamp(2.35rem, 10.5vw, 2.75rem); line-height: 0.96; }
  .hero-grid .lead { margin-bottom: 12px; }
  .hero-grid .hero-trust { margin-bottom: 20px; }
  /* Two full-width buttons, primary first: a thumb-sized target each, and the pair reads as
     one decision rather than two pills of different widths. */
  .hero-grid .hero-actions { flex-direction: column; align-items: stretch; gap: 12px; }
  /* The glass button sits over the moving footage here; backdrop-filter would re-blur it on
     every decoded frame, so phones get a plain translucent fill instead. */
  .hero-grid .hero-actions .btn-glass { backdrop-filter: none; -webkit-backdrop-filter: none; background: rgba(18,18,22,0.72); }
}
/* Desktop: centre the copy in the part of the first screen that is visible. The hero is
   one viewport tall but starts below the 117px top bar + header, so plain centring sat
   the copy 58px low and cut the CTAs off in maximised laptop browsers (e.g. 1366x641). */
@media (min-width: 53.8125em) {
  .hero-glow .hero-scrub-sticky { padding-bottom: 117px; }
}
/* The lead is one sentence; balancing it keeps a lone word off its last line. */
.hero-grid .lead { text-wrap: balance; }
.hero-video-toggle {
  position: absolute; right: max(20px, env(safe-area-inset-right)); bottom: 20px; z-index: 3;
  width: 44px; height: 44px; border-radius: 50%; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(10,10,12,0.6); border: 1px solid rgba(255,255,255,0.28); color: var(--white);
  cursor: pointer; transition: background-color var(--dur-base) var(--ease-out);
}
.hero-video-toggle[hidden] { display: none; }
.hero-video-toggle svg { width: 18px; height: 18px; fill: currentColor; }
.hero-video-toggle .icon-play, .hero-video-toggle.is-paused .icon-pause { display: none; }
.hero-video-toggle.is-paused .icon-play { display: block; }
@media (any-hover: hover) { .hero-video-toggle:hover { background: rgba(10,10,12,0.85); } }
/* The hero is one viewport tall but starts below the 117px top bar + header, so a plain
   bottom: 20px put the control under the fold. Wherever the fixed Call bar shows (<=620px)
   the hero's foot is behind it or below the fold, so there the control hangs from the top. */
@media (min-width: 621px) { .hero-video-toggle { bottom: calc(117px + 20px); } }
@media (max-width: 620px), (max-height: 520px), (max-width: 860px) and (orientation: portrait) { .hero-video-toggle { right: max(12px, env(safe-area-inset-right)); top: 12px; bottom: auto; } }
.hero-grid .hero-actions .btn:focus-visible {
  outline: 2px solid var(--white); outline-offset: 3px;
}

.hero-badge {
  display: inline-flex; align-items: center; gap: 7px;
  background: rgba(255,255,255,0.07); border: 1px solid rgba(255,255,255,0.14);
  padding: 5px 14px; border-radius: var(--pill); font-size: var(--fs-xs); font-weight: 500;
  letter-spacing: 0.02em; color: rgba(255,255,255,0.75);
  margin-bottom: 26px;
}
.hero-glow h1 {
  color: var(--white); margin-bottom: 0.3em;
  font-family: 'Manrope', var(--font); font-weight: 800; letter-spacing: -0.035em;
  text-shadow: 0 2px 24px rgba(0,0,0,0.35);
}
.hero-glow p.lead { color: rgba(255,255,255,0.68); max-width: 48ch; margin: 0 auto 34px; }
.hero-actions { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; }


/* Page hero (secondary pages) - one component, Services through Contact.
   Flat near-black instead of the old red/navy wash: the homepage video is the
   site's one cinematic moment, and these pages relate to it through the same
   dark ground and type, not through a second, louder gradient. */
.page-hero {
  position: relative; overflow: hidden;
  background: var(--near-black);
  color: var(--white); padding: clamp(56px, 7vw, 92px) 0 clamp(56px, 6.5vw, 88px);
  text-align: center;
  border-bottom: 1px solid rgba(255,255,255,0.06);
}
.page-hero .container { position: relative; z-index: 1; }
.page-hero h1 { color: var(--white); margin-bottom: 10px; }
.page-hero p { color: rgba(255,255,255,0.68); margin: 0; font-size: 1.1rem; }

/* Split layout: copy left (~55%), page-specific visual right (~45%). Below
   900px it stacks copy-then-visual - the headline is what the page is, the
   picture only supports it - rather than shrinking two columns until both
   are too small to read. */
.page-hero--split { text-align: left; }
.page-hero--split .container { display: grid; gap: 36px; }
/* A grid item won't shrink below its longest word by default; at large text sizes that
   pushed the heading and rating pill past the hero's clipped edge. */
.page-hero--split .page-hero-copy { min-width: 0; }
.page-hero--split h1 {
  font-size: clamp(2.6rem, 5.2vw, 4.5rem); line-height: 1.02; margin-bottom: 18px;
}
.page-hero--split p { max-width: 44ch; font-size: clamp(1.05rem, 1.4vw, 1.2rem); line-height: 1.55; }
.page-hero--split .hero-badge { margin-bottom: 24px; }
/* The red accent: one small mark per hero, not a wash. */
.page-hero--split .hero-badge > .icon { color: var(--red); }
@media (min-width: 900px) {
  .page-hero--split .container {
    grid-template-columns: minmax(0, 11fr) minmax(0, 9fr);
    gap: clamp(40px, 5vw, 80px); align-items: center;
  }
}
.page-hero-visual {
  position: relative; margin: 0;
  aspect-ratio: 4 / 3; border-radius: var(--radius); overflow: hidden;
  background: var(--dark-surface);
  border: 1px solid rgba(255,255,255,0.08);
  box-shadow: 0 40px 80px -40px rgba(0,0,0,0.75);
}
.page-hero-visual picture { display: block; height: 100%; }
.page-hero-visual img { width: 100%; height: 100%; object-fit: cover; }
/* Soft dark overlay and edge fade, so the photo settles into the ground
   instead of reading as a rectangle pasted beside the text. */
.page-hero-visual::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
  background: rgba(10,10,12,0.10);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.04), inset 0 0 80px 8px rgba(10,10,12,0.55);
}
@media (max-width: 899px) { .page-hero--split .page-hero-visual { max-width: 560px; } }

/* Photo hero (Services, Shop, About, Contact). The photograph is the hero's
   environment rather than a framed card beside the copy: it fills the right
   of the section and dissolves into the near-black under the text. The
   gradient exists for legibility and the seam, not decoration. Testimonials
   keeps its approved split composition above. */
.page-hero--photo { text-align: left; padding: 0; display: flex; align-items: center; min-height: clamp(480px, 38vw, 600px); }
.page-hero--photo .page-hero__content { position: relative; z-index: 1; width: 100%; padding-top: 72px; padding-bottom: 72px; }
.page-hero--photo .page-hero-copy { max-width: 560px; }
.page-hero--photo h1 { font-size: clamp(2.6rem, 5.2vw, 4.5rem); line-height: 1.02; margin-bottom: 18px; }
.page-hero--photo p { max-width: 44ch; font-size: clamp(1.05rem, 1.4vw, 1.2rem); line-height: 1.55; color: rgba(255,255,255,0.72); }
.page-hero--photo .hero-badge { margin-bottom: 24px; background: rgba(10,10,12,0.55); }
.page-hero--photo .hero-badge > .icon { color: var(--red); }
.page-hero__media { position: absolute; top: 0; right: 0; bottom: 0; width: 66%; margin: 0; }
.page-hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: var(--hero-focus, 60% 50%); }
.page-hero__media::after {
  /* -1px on the left and a solid first stretch: without both, the photo's
     sub-pixel edge showed as a hairline seam down the hero. */
  content: ""; position: absolute; inset: 0 0 0 -1px; pointer-events: none;
  background:
    linear-gradient(90deg, var(--near-black) 0%, var(--near-black) 4%, rgba(10,10,12,0.86) 18%, rgba(10,10,12,0.45) 42%, rgba(10,10,12,0.12) 70%, rgba(10,10,12,0.18) 100%),
    linear-gradient(0deg, var(--near-black) 0%, rgba(10,10,12,0) 22%),
    linear-gradient(180deg, rgba(10,10,12,0.5) 0%, rgba(10,10,12,0) 20%);
}
.page-hero--services { --hero-focus: 55% 50%; }
.page-hero--about    { --hero-focus: 62% 50%; }
.page-hero--shop     { --hero-focus: 55% 60%; }
.page-hero--contact  { --hero-focus: 70% 50%; }
/* 320px: the headline ran to three lines ("fixed" alone on the last), pushing the number
   and hours behind the call bar on the first screen. */
@media (max-width: 22.5em) { .page-hero--contact h1 { font-size: 2.25rem; text-wrap: balance; } }
/* Services carries a two-sentence headline, not a two-word title: a step smaller and
   balanced, so it does not stack into four ragged lines in the 560px copy column. */
.page-hero--services h1 { font-size: clamp(2.3rem, 4.4vw, 3.5rem); text-wrap: balance; }
/* "We'll assess what can be done." starts its own line rather than splitting after "We'll". */
@media (min-width: 360px) { .page-hero--services h1 .sentence { display: inline-block; } }
/* Phones and small tablets: the photograph becomes a band across the top
   that fades into the copy, instead of sitting behind text it would fight. */
@media (max-width: 899px) {
  .page-hero--photo { display: block; min-height: 0; }
  /* display: block - a <picture> is inline once it isn't absolutely positioned, and an
     inline box ignores aspect-ratio and max-height, so neither cap ever applied. */
  .page-hero__media { display: block; position: relative; width: 100%; height: auto; aspect-ratio: 16 / 9; max-height: 420px; }
  .page-hero__media::after {
    background:
      linear-gradient(0deg, var(--near-black) 0%, rgba(10,10,12,0.55) 28%, rgba(10,10,12,0) 60%),
      linear-gradient(180deg, rgba(10,10,12,0.35) 0%, rgba(10,10,12,0) 30%);
  }
  .page-hero--photo .page-hero__content { margin-top: -44px; padding-top: 0; padding-bottom: 52px; }
}
/* Landscape phones: the 16:9 band was taller than the screen, so the first screen showed
   only a photograph and the heading started below the fold. */
@media (max-width: 899px) and (max-height: 500px) {
  .page-hero__media { max-height: 42svh; }
}

/* Compact rating pill (Testimonials): the star row and the figure it
   stands for, side by side - never a lone star either side of 4.9. */
.page-hero .hero-badge--rating { gap: 10px; padding-top: 6px; padding-bottom: 6px; }
/* At large text sizes the stars and the figure wrap onto two lines instead of running past
   the hero's edge, where overflow: hidden would cut them off. */
.page-hero .hero-badge--rating { max-width: 100%; flex-wrap: wrap; row-gap: 4px; }

/* Legal pages - the plain dark hero, smaller and left-aligned on the same 720px
   column as the text below, so a policy reads as a document, not a campaign. */
.page-hero--legal { text-align: left; padding: clamp(44px, 6vw, 72px) 0 clamp(40px, 5vw, 60px); }
.page-hero--legal .legal-hero-inner { max-width: 720px; margin: 0 auto; }
.page-hero--legal h1 { font-size: clamp(2.2rem, 4.6vw, 3.4rem); line-height: 1.05; letter-spacing: -0.03em; text-wrap: balance; margin-bottom: 14px; }
.page-hero--legal p { max-width: 56ch; font-size: clamp(1.02rem, 1.4vw, 1.15rem); line-height: 1.55; }
.page-hero--legal .legal-updated { margin-top: 18px; font-size: 0.85rem; color: rgba(255,255,255,0.55); }

/* Legal document - the site's 18px body on a 720px column; headings clear the sticky header when a
   contents link jumps to them. */
.legal-doc { max-width: 720px; margin: 0 auto; font-size: 1.125rem; line-height: 1.7; }
.legal-doc p, .legal-doc li { color: var(--text-soft); }
.legal-doc p, .legal-doc ul, .legal-doc ol { margin: 0 0 16px; }
.legal-doc ul, .legal-doc ol { padding-left: 1.3em; }
.legal-doc li + li { margin-top: 6px; }
.legal-doc strong { color: var(--text); font-weight: 600; }
.legal-doc h2 { font-size: clamp(1.4rem, 2.6vw, 1.75rem); letter-spacing: -0.02em; margin: 52px 0 14px; }
.legal-doc h3 { font-size: 1.125em; margin: 26px 0 8px; }
.legal-doc a:not(.btn) { color: var(--red-dark); text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }
@media (any-hover: hover) { .legal-doc a:not(.btn):hover { color: var(--red); } }
.legal-intro { font-size: 1.08em; }
.legal-toc, .legal-summary { background: var(--gray-50); border-radius: var(--radius); padding: 22px 26px; margin: 28px 0 8px; }
.legal-toc h2, .legal-summary h2 { font-size: 0.95rem; letter-spacing: 0; margin: 0 0 10px; }
.legal-doc .legal-summary ul { margin: 0; }
.legal-doc .legal-toc ol { margin: 0; padding-left: 1.4em; columns: 2 16rem; column-gap: 32px; }
.legal-doc .legal-toc li { margin: 0 0 6px; break-inside: avoid; }
.legal-doc .legal-toc a { color: var(--text); text-decoration: none; }
@media (any-hover: hover) { .legal-doc .legal-toc a:hover { color: var(--red); text-decoration: underline; } }
/* The contents list is a <details>: open, and not a control, above 600px (it looks as it
   always did); on phones it starts closed (js/script.js) and opens from its heading, so the
   reader isn't scrolled through 600px of grey panels before section 1. */
.legal-toc summary { display: block; list-style: none; }
.legal-toc summary::-webkit-details-marker { display: none; }
@media (min-width: 601px) { .legal-toc summary { pointer-events: none; } }
/* Set-apart statements: the Australian Consumer Law texts and the repair notice must be
   easy to see and visibly separate from the surrounding terms. */
.legal-box { border: 1px solid var(--gray-100); border-left: 4px solid var(--red); border-radius: var(--radius-sm); background: var(--paper); padding: 20px 24px; margin: 20px 0 24px; }
.legal-box h3 { margin-top: 0; }
.legal-box p:last-child, .legal-box ul:last-child { margin-bottom: 0; }
.legal-box .legal-box__label { display: block; font-size: 0.78rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--gray-500); margin-bottom: 8px; }
.legal-contact { background: var(--gray-50); border-radius: var(--radius); padding: 22px 26px; }
.legal-contact ul { list-style: none; padding: 0; margin: 0; }
/* Review notes for the owner: hidden unless js/script.js marks the page as a preview
   or local copy, so a launch that forgets to strip them can't show them to customers. */
.legal-draft, .legal-flag { display: none; }
.show-review-notes .legal-draft { display: block; background: #fff7e6; border: 1px solid #f0c36d; color: #5c4400; border-radius: var(--radius-sm); padding: 14px 18px; margin: 0 0 24px; font-size: 0.9rem; }
.show-review-notes .legal-flag { display: block; border: 1px dashed #d9a441; background: #fffbf2; color: #5c4400; border-radius: var(--radius-sm); padding: 10px 14px; margin: 12px 0 20px; font-size: 0.86rem; line-height: 1.55; }
.show-review-notes .legal-draft p, .show-review-notes .legal-flag p { color: inherit; margin: 0; }
@media (max-width: 600px) {
  .legal-doc > h2 { margin-top: 40px; }
  .legal-toc, .legal-summary, .legal-contact { padding: 18px 20px; }
  .legal-box { padding: 16px 18px; }
  /* 17px on a ~40-character line instead of 18px on 28-42: the document was 14 screens.
     The intro keeps its emphasis by colour rather than by being larger than the hero's. */
  .legal-doc { font-size: 1.0625rem; line-height: 1.62; }
  .legal-intro { font-size: 1em; color: var(--text); }
  /* The box labels were 15px over 18px text; they head the boxes now. */
  .legal-toc h2, .legal-summary h2 { font-size: 1rem; font-weight: 700; }
  .legal-toc { padding-top: 0; padding-bottom: 0; }
  .legal-toc summary {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    min-height: 52px; cursor: pointer;
  }
  .legal-toc summary h2 { margin: 0; }
  .legal-toc summary::after {
    content: ""; flex: none; width: 8px; height: 8px; margin-top: -4px;
    border-right: 2px solid var(--gray-450); border-bottom: 2px solid var(--gray-450); transform: rotate(45deg);
  }
  .legal-toc details[open] summary::after { margin-top: 4px; transform: rotate(-135deg); }
  .legal-doc .legal-toc ol { padding-bottom: 12px; }
  .legal-doc .legal-toc li { margin: 0; }
  /* 40px rows: the links were only as tall as their text. */
  .legal-doc .legal-toc a { display: block; padding: 8px 0; }
  /* A number, the hours and each related page stay whole ("07 3162 / 4168", "Returns, /
     Refunds & Warranty" read as two links). */
}
@media (max-width: 600px) and (min-width: 20em) {
  .legal-contact a[href^="tel:"], .legal-related a { white-space: nowrap; }
}
@media print {
  .site-header, .topbar, .mobile-call-bar, .back-to-top, .legal-toc, .site-footer { display: none !important; }
  .page-hero--legal { background: none; color: #000; padding: 0 0 12px; border: 0; }
  .page-hero--legal h1, .page-hero--legal p, .page-hero--legal .legal-updated { color: #000; }
}

/* Testimonials visual, built from the real review system: the Google
   aggregate on a raised card, with two quieter cards behind and beside it -
   a verbatim sentence from a review shown further down the page, carrying
   that review's own verified rating, and the award history. Nothing in this
   picture is a review or a rating that was not actually given. */
.review-visual { container-type: inline-size; aspect-ratio: auto; }
.review-visual::after { background: none; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.04); }
/* The 4:3 shape lives on the stage, not the panel, so a narrow panel can
   grow to fit its cards stacked instead (a container query cannot restyle
   its own container, only what is inside it). */
.rv-stage { position: relative; aspect-ratio: 4 / 3; }
.rv-card {
  position: absolute; width: 54%;
  display: flex; flex-direction: column; gap: 2.4cqi;
  padding: 5.5cqi 6cqi;
  border-radius: 14px; background: rgba(255,255,255,0.035);
  border: 1px solid rgba(255,255,255,0.08);
}
.rv-card--back { top: 9%; right: 6%; }
.rv-card--front { bottom: 9%; right: 13%; background: #1a1a1d; }
.rv-quote {
  display: block; height: 7cqi; font-family: Georgia, serif; font-size: 15cqi; line-height: 0.85;
  color: var(--red);
}
.rv-score {
  position: absolute; left: 7%; top: 50%; transform: translateY(-50%); z-index: 2;
  display: flex; flex-direction: column; align-items: flex-start; gap: 2.2cqi;
  padding: 5.5cqi 6cqi; border-radius: 16px;
  background: #1f1f23; border: 1px solid rgba(255,255,255,0.12);
  box-shadow: 0 30px 60px -24px rgba(0,0,0,0.8);
}
.rv-source { display: inline-flex; align-items: center; gap: 8px; font-size: 0.8rem; font-weight: 600; color: rgba(255,255,255,0.72); }
.rv-g {
  width: 24px; height: 24px; border-radius: 7px; background: var(--white);
  display: flex; align-items: center; justify-content: center;
}
.rv-g .icon { width: 15px; height: 15px; stroke: none; }
.rv-number {
  font-size: clamp(2.4rem, 12cqi, 3.6rem); font-weight: 700; letter-spacing: -0.03em;
  line-height: 1; font-variant-numeric: tabular-nums; color: var(--white);
}
.rv-number small { font-size: 0.4em; font-weight: 500; letter-spacing: 0; color: rgba(255,255,255,0.5); margin-left: 3px; }
.rv-count { font-size: 0.8rem; color: rgba(255,255,255,0.55); }
/* The excerpt card sits behind the Google card's top edge; its left padding
   keeps the sentence clear of the part the Google card covers. */
.rv-excerpt { top: 7%; right: 4%; width: 60%; gap: 2cqi; padding-left: 12cqi; }
.rv-excerpt__text {
  display: block; color: rgba(255,255,255,0.86); font-weight: 500;
  font-size: clamp(0.76rem, 3cqi, 0.95rem); line-height: 1.45;
}
.rv-excerpt__meta {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px;
  font-size: clamp(0.75rem, 2.6cqi, 0.8rem); color: rgba(255,255,255,0.55);
}
/* The smallest of the three: one fact, no image larger than a chip. Capped at
   44% from a 4% right edge so its left side stays at 52%+, clear of the
   Google card (which ends by ~46%) by a deliberate gap, not a hairline. */
.rv-award {
  bottom: 9%; right: 4%; width: auto; max-width: 44%; z-index: 3;
  flex-direction: row; align-items: center; gap: 3cqi; padding: 3.6cqi 4.4cqi;
  box-shadow: 0 20px 40px -24px rgba(0,0,0,0.7);
}
/* Scoped through .review-visual: the hero's generic `.page-hero-visual img`
   (100% x 100%, for photographs) would otherwise blow the logo up to full size. */
.review-visual .rv-award__logo {
  width: clamp(28px, 8cqi, 36px); height: auto; flex: none; object-fit: contain;
  padding: 3px; border-radius: 8px; background: var(--white);
}
.rv-award__text { display: flex; flex-direction: column; line-height: 1.3; }
.rv-award__text strong { font-size: clamp(0.75rem, 2.9cqi, 0.9rem); font-weight: 600; color: rgba(255,255,255,0.88); }
.rv-award__text span { font-size: clamp(0.75rem, 2.6cqi, 0.8rem); color: rgba(255,255,255,0.55); }
.rv-award__text .rv-award__years { font-size: inherit; color: inherit; white-space: nowrap; }
/* A small panel (phones, and two-column desktops below ~1200px, where the
   panel is under 470px) cannot hold three cards side by side: the Google
   card has a minimum size and would cover the others. So the stage drops its
   4:3 shape and the cards stack - Google card first, the excerpt (with the
   red quote mark) tucked under its corner, then the award card - keeping all
   three and the layered look at every width. */
@container (max-width: 469px) {
  .rv-stage { aspect-ratio: auto; display: flex; flex-direction: column; gap: 14px; padding: 6cqi; }
  .rv-stage > .rv-card,
  .rv-stage > .rv-score { position: relative; inset: auto; transform: none; width: auto; max-width: none; }
  .rv-stage > .rv-score { order: 0; align-self: flex-start; z-index: 2; }
  /* Overlaps the Google card's bottom edge by 26px; the matching top padding
     keeps the quote mark and the sentence below that overlap. */
  .rv-stage > .rv-excerpt { order: 1; align-self: flex-end; width: 86%; margin-top: -26px; padding: calc(26px + 4cqi) 6cqi 5cqi; }
  .rv-stage > .rv-award { order: 2; align-self: flex-start; }
}

/* Giant stat strip (Apple spec-sheet style) */
.stat-strip { display: flex; justify-content: center; gap: clamp(32px, 8vw, 96px); flex-wrap: wrap; text-align: center; }
.stat-strip strong { display: block; font-size: clamp(2.4rem, 5vw, 3.6rem); font-weight: 600; letter-spacing: -0.02em; color: var(--text); font-variant-numeric: tabular-nums; }
/* Labels only: the figure can carry spans of its own (the live rating and its star row). */
.stat-strip strong ~ span { color: var(--gray-500); font-size: 0.92rem; }
/* Homepage story box: the figures ("90 days", "3–12") stay on one line; on narrower screens the
   label stacks under its number instead of squeezing beside it. */
/* One grid across all three rows (the row divs get display: contents), so every label
   starts in the same column whatever the width of its number. Used on index and about. */
.story-stats { display: grid; grid-template-columns: max-content 1fr; align-items: baseline; gap: 28px 16px; text-align: left; }
.story-stats > div { display: contents; }
.story-stats strong { font-size: 2.6rem; white-space: nowrap; }
@media (max-width: 1000px) {
  .story-stats { display: flex; flex-direction: column; gap: 28px; }
  .story-stats > div { display: flex; flex-direction: column; gap: 2px; }
}
.tile.story-stats-tile { padding: 44px 36px; }
/* Phones: a short divided list - figure left, label beside it - instead of three tall
   stacked rows inside a deep card. Only where a 320px-wide screen has room at the reader's
   text size (20em = 320px at the default 16px): with the browser's text at 200% the figure
   alone fills the row, so it keeps the stacked layout above instead of running off the
   screen. The same em guard is on the other phone row layouts below. */
@media (max-width: 600px) { .tile.story-stats-tile { padding: 24px 22px; } }
@media (max-width: 600px) and (min-width: 20em) {
  .tile.story-stats-tile { padding: 8px 22px; }
  .story-stats { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 0 16px; align-items: center; }
  .story-stats > div { display: contents; }
  .story-stats > div > * { padding: 16px 0; }
  .story-stats > div + div > * { border-top: 1px solid var(--gray-100); }
  .story-stats strong { font-size: 2rem; }
}

/* Proof panel — dark upgrade of the homepage stat strip. Same real numbers,
   presented as a dedicated section with a one-time count-up (js/script.js)
   instead of a plain inline row. */
/* Trust rail — a continuation of the hero's dark chapter, not an object
   sitting on a light page. The four figures now live inside one outlined
   shell: edged rather than filled, so it reads as a surface cut out of the
   same dark rather than a black box dropped onto it. One shell, four
   statistics - deliberately not four separate bubbles. */
.trust-rail {
  background: var(--near-black); color: var(--white);
  padding: 40px 0 46px;
  border-bottom: 1px solid rgba(255,255,255,0.10);
}
.trust-shell {
  border: 1px solid rgba(255,255,255,0.14);
  background: rgba(255,255,255,0.035);
  border-radius: 28px;
  padding: 30px 32px;
}
.trust-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 26px 20px; }
@media (min-width: 860px) {
  .trust-grid { grid-template-columns: repeat(4, 1fr); gap: 0; }
  /* Divider between items rather than around them. */
  .trust-cell + .trust-cell { border-left: 1px solid rgba(255,255,255,0.10); padding-left: 30px; }
}
/* Below the four-up breakpoint the 2x2 needs its own seam between rows. */
@media (max-width: 859px) {
  .trust-cell:nth-child(n+3) { border-top: 1px solid rgba(255,255,255,0.10); padding-top: 24px; }
  .trust-cell:nth-child(2n) { border-left: 1px solid rgba(255,255,255,0.10); padding-left: 20px; }
}
.trust-cell strong {
  display: block; font-size: clamp(1.9rem, 3.2vw, 2.6rem); font-weight: 700;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--white);
  line-height: 1.05;
}
.trust-cell > span {
  display: block; margin-top: 8px; font-size: 0.86rem; line-height: 1.45;
  color: rgba(255,255,255,0.6);
}
@media (max-width: 859px) { .trust-rail { padding: 32px 0 36px; } .trust-shell { padding: 24px 22px; border-radius: 24px; } }

/* Hero enhancements — eyebrow tag, trust rating row, faint background
   wordmark, and a floating service
   card. All hand off opacity with the rest of #hero-scrub-text
   (js/script.js) so nothing fights the scrub reveal for the same space. */
.hero-eyebrow {
  display: flex; width: fit-content; align-items: center; gap: 8px;
  font-size: 0.82rem; font-weight: 500; color: rgba(255,255,255,0.65);
  margin-bottom: 14px;
}
.hero-eyebrow .dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,0.5); }

.hero-watermark {
  position: absolute; inset-inline: 0; bottom: 6%; z-index: 1;
  display: flex; flex-direction: column; align-items: center; text-align: center;
  pointer-events: none; user-select: none;
  font-family: 'Manrope', var(--font); font-weight: 800; line-height: 0.86; letter-spacing: -0.03em;
  color: rgba(255,255,255,0.05);
}
.hero-watermark span { font-size: clamp(3.5rem, 11vw, 9rem); }



/* Sections */
.section { padding: 64px 0; }
.section-tight { padding: 44px 0; }
@media (max-width: 760px) {
  .section { padding: 48px 0; }
  .section-tight { padding: 34px 0; }
}
/* Light chapter — the single deliberate break out of the dark hero/trust
   chapter. A flat colour, not a gradient: .section-alt fades white -> paper
   -> white at its edges, which is the soft, undecided seam this replaces.
   .section-alt itself is left alone because about.html and contact.html
   use it. */
.chapter-light { background: var(--surface-light); }

/* Homepage service cards — photograph first, then title, then one line.
   2x2 rather than four narrow columns so the photography carries real
   visual weight. No badge, no icon tile, no index numeral, no gradient chip.
   These carry photographs rather than icon bubbles, so the bubble sizing in
   the brief does not apply here; the 15-20% reduction is taken out of
   padding, radius, gap and type instead. */
.service-cards {
  display: grid; grid-template-columns: 1fr; gap: 16px;
}
@media (min-width: 760px) { .service-cards { grid-template-columns: repeat(2, 1fr); gap: 18px; } }
.service-card {
  position: relative;
  background: var(--white);
  border: 1px solid rgba(0,0,0,0.08);
  border-radius: 19px;
  overflow: hidden;
  padding-bottom: 18px;
  box-shadow: 0 1px 2px rgba(11,18,32,0.035);
  transition:
    background-color 200ms var(--ease-out),
    border-color 200ms var(--ease-out),
    transform 200ms var(--ease-out),
    box-shadow 200ms var(--ease-out);
}
/* Fixed ratio reserves the space before the image decodes, so the grid
   cannot shift, and keeps the crop identical across all four cards. */
.service-card-media { aspect-ratio: 4 / 3; background: var(--gray-100, #ececed); }
.service-card-media img { display: block; width: 100%; height: 100%; object-fit: cover; }
.service-card h3 { margin: 17px 22px 5px; font-size: 1rem; letter-spacing: -0.01em; }
.service-card p { margin: 0 22px; font-size: 0.88rem; line-height: 1.5; color: var(--text-soft); }

/* The heading link stretches over the whole card, so the hover promises a
   click that actually goes somewhere and keyboard users get a real stop. */
.tile-link { color: inherit; text-decoration: none; }
.tile-link::before { content: ""; position: absolute; inset: 0; z-index: 1; border-radius: inherit; }
.tile-link:focus-visible { outline: none; }

/* Keyboard users get the same read as pointer users. */
.service-card:has(.tile-link:focus-visible) {
  background: rgba(230,57,70,0.045);
  border-color: rgba(230,57,70,0.3);
  outline: 2px solid var(--red); outline-offset: 3px;
}
/* any-hover, not (hover: hover) and (pointer: fine): those test only the
   PRIMARY pointer, and a touchscreen laptop can report touch as primary -
   which switched these hovers off for its touchpad and mouse entirely. */
@media (any-hover: hover) {
  .service-card:hover {
    background: rgba(230,57,70,0.045);
    border-color: rgba(230,57,70,0.3);
    transform: translateY(-2px);
    box-shadow: 0 5px 14px rgba(11,18,32,0.07);
  }
}
@media (prefers-reduced-motion: reduce) {
  .service-card { transition: background-color 200ms linear, border-color 200ms linear; }
  .service-card:hover { transform: none; }
}
/* Phones: four photograph-first cards stacked at full width ran to ~1,400px of scrolling.
   Here each is a row - photograph left, title and line right - so all four read as one
   list on about one screen. The photograph is cropped, never stretched. Up to 759px, where
   the two-column cards begin: between the two, each card was a 543-598px photograph, taller
   than a landscape phone's screen. */
@media (max-width: 759px) {
  .service-cards { gap: 12px; }
  .service-card {
    display: grid; grid-template-columns: min(38%, 240px) minmax(0, 1fr); grid-template-rows: auto 1fr;
    column-gap: 16px; padding-bottom: 0; border-radius: 16px;
  }
  .service-card-media { grid-row: 1 / span 2; aspect-ratio: auto; height: 100%; min-height: 112px; }
  .service-card h3 { margin: 16px 14px 4px 0; text-wrap: balance; }
  .service-card p { margin: 0 14px 16px 0; font-size: 0.86rem; text-wrap: pretty; }
}
@media (min-width: 601px) and (max-width: 759px) { .service-card-media { min-height: 132px; } }
/* 320px: a narrower photograph gives the words a 166px column instead of 139px. */
@media (max-width: 374px) {
  .service-card { grid-template-columns: 30% minmax(0, 1fr); column-gap: 12px; }
  .service-card h3, .service-card p { margin-right: 12px; }
}
/* Touch has no hover, so a card answers the press instead. */
@media (any-pointer: coarse) {
  .service-card:active { background: rgba(230,57,70,0.045); border-color: rgba(230,57,70,0.3); }
}


/* Homepage spacing. Scoped to .home because .section, .section-tight and
   .section-head are shared by all seven pages, and this is a homepage
   density pass - not a global type-scale change. */
.home .section { padding: 64px 0; }
.home .section-tight { padding: 42px 0; }
.home .section-head { margin-bottom: 32px; }
.home .grid { gap: 18px; }
@media (max-width: 720px) {
  .home .section { padding: 48px 0; }
  .home .section-tight { padding: 34px 0; }
  .home .section-head { margin-bottom: 24px; }
  .home .grid { gap: 15px; }
}
.section-alt {
  background: linear-gradient(180deg, var(--white) 0%, var(--paper) 40px, var(--paper) calc(100% - 40px), var(--white) 100%);
}
.section-dark {
  position: relative; z-index: 2;
  background: linear-gradient(180deg, var(--near-black), var(--navy));
  color: var(--white);
}
.section-dark .container { position: relative; z-index: 1; }

/* Seam-blend — softens the boundary between a light section and a dark one,
   the way an airbrush with a soft round tip and low opacity builds up
   colour gradually: several overlapping soft-edged blurred dabs instead of
   one hard-edged gradient band, composited with real blend modes (multiply
   to darken the light side, screen to lighten the dark side) rather than
   flat alpha, so it picks up in-between tones instead of a muddy straight
   line. Lives as a pseudo-element ON the dark section itself (not a
   separate sibling) and bleeds past its own edge via a negative offset —
   giving .section-dark real stacking priority (z-index above) is what lets
   that bleed paint over the neighbouring section instead of being hidden
   underneath it. z-index 0 here keeps it below the section's own text. */
/* The curve overlays that used to sit here are gone. They were absolutely
   positioned at top/bottom: -59px, so a near-black shape rose 59px into
   the section above and sat on top of its content - on services.html that
   was the last row of service tiles. Chapters are marked by the background
   change itself. .section-curve-top/-bottom elements left in the markup
   now render nothing. */
.section-curve-top, .section-curve-bottom { display: none; }

/* A blurred multiply overlay used to sit here, 180px tall at top: -90px,
   so it reached 90px above the section and darkened whatever was there -
   on services.html, a soft grey smudge across the bottom row of tiles.
   The seam it was softening no longer needs softening: the sections meet
   on a clean colour change. */
.section-dark h2, .section-dark h3 { color: var(--white); }
.section-dark p { color: rgba(255,255,255,0.68); }
.section-head { max-width: 640px; margin: 0 auto 34px; text-align: center; }
/* Section headings are short sentences; balanced, none ends on a lone word
   ("Three decades at the / workbench."). Line count never changes, only where
   the break falls, so no section moves. */
.section-head h2, .grid-2 h2, .cta-bubble h2 { text-wrap: balance; }
/* Small phones: at the 2rem floor a 272px measure broke these into three lines with a lone
   word ("What makes / Laptop / Clinic different"), and closing-band headings into four. */
@media (max-width: 400px) { .section-head h2:not(.h2--small), .grid-2 h2, .cta-bubble h2 { font-size: 1.75rem; } }
/* Intro paragraphs: pretty, not balance - keeps the measure but never leaves one word
   ("inspect.") alone on the last line. */
.section-head p { text-wrap: pretty; }
@media (max-width: 760px) { .section-head { margin-bottom: 26px; } }
.eyebrow {
  display: inline-block; color: var(--red-dark); font-weight: 600; font-size: 0.78rem;
  letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 12px;
}
/* On the dark surfaces --red-dark drops to 3.3-3.5:1; --red is 4.5:1 or better there. */
.page-hero .eyebrow, .cta-bubble .eyebrow { color: var(--red); }
.category-label {
  display: flex; align-items: center; gap: 16px; width: 100%;
  color: var(--text); font-weight: 700;
}
.category-label::after { content: ''; flex: 1; height: 1px; background: var(--gray-200); }

/* A distinctive display font used sparingly, on standout headings only */
.accent-heading { font-family: 'Manrope', var(--font); font-weight: 700; letter-spacing: -0.02em; }

/* Grids & tiles (Microsoft-style rounded cards) */
.grid { display: grid; gap: 22px; }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 900px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .grid-3, .grid-4, .grid-2 { grid-template-columns: 1fr; } }
/* Story sections (index, About): copy beside the stats box. On phones the 60px desktop gap
   becomes a section gap, the lead stops being smaller than the body text under it (1.1rem
   against 18px), and the two buttons share one width instead of ending ragged. */
.grid-2.story-grid { align-items: center; gap: 60px; }
.hero-actions.story-actions { justify-content: flex-start; }
@media (max-width: 620px) {
  .grid-2.story-grid { gap: 32px; }
  .story-grid .lead { font-size: 1.2rem; color: var(--text); }
  .hero-actions.story-actions { flex-direction: column; align-items: stretch; gap: 12px; }
}
/* About's "What makes Laptop Clinic different": on phones the icon sits beside the
   statement, like a list, rather than on its own line above it. At tablet widths the
   third tile spans the row instead of sitting alone beside an empty half. */
@media (max-width: 620px) {
  .why-grid { gap: 14px; }
  .why-grid .tile { display: grid; grid-template-columns: 44px minmax(0, 1fr); column-gap: 16px; padding: 22px 20px; }
  .why-grid .tile .badge { grid-row: 1 / span 2; width: 44px; height: 44px; margin: 0; border-radius: 12px; }
  .why-grid .tile .badge .icon { width: 22px; height: 22px; }
}
@media (min-width: 621px) and (max-width: 900px) {
  .why-grid > :last-child:nth-child(odd),
  .reviews-grid > :last-child:nth-child(odd),
  .contact-methods > :last-child:nth-child(odd),
  .not-found-links > :last-child:nth-child(odd) { grid-column: 1 / -1; }
}

.tile {
  background: var(--white);
  border: 1px solid var(--gray-100);
  border-radius: var(--radius);
  padding: 30px;
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.25s ease, transform 0.25s ease;
}
@media (any-hover: hover) {
  .tile:hover { box-shadow: var(--shadow-md); transform: translateY(-4px); }
}
.tilt-card { position: relative; z-index: 1; transition: box-shadow 0.25s ease; }
@media (any-hover: hover) { .tilt-card:hover { z-index: 4; box-shadow: var(--shadow-lg), 0 0 0 1px rgba(230,57,70,0.15); } }
.tile .badge {
  width: 50px; height: 50px; border-radius: 14px;
  display: flex; align-items: center; justify-content: center; margin-bottom: 20px;
}
.tile .badge .icon { width: 24px; height: 24px; }
.badge-red { background: var(--red-tint); color: var(--red); }
.badge-blue { background: var(--blue-tint); color: var(--blue); }
.badge-navy { background: rgba(11,18,32,0.06); color: var(--navy); }
/* 1.25, not the display 1.08: tile titles run to two or three lines on phones. */
.tile h3 { margin-bottom: 6px; font-size: 1.1rem; line-height: 1.25; text-wrap: balance; }
/* Keeps a lone word off the last line of a tile's description where the browser supports it. */
.tile p { text-wrap: pretty; }
/* A section heading that sits inside a section rather than heading it. */
.h2--small { font-size: 1.5rem; }
/* 404: its tiles follow the h1 directly, so they are h2s set at the tile size. */
.tile .tile-title { margin-bottom: 6px; font-size: 1.1rem; line-height: 1.25; letter-spacing: -0.01em; }
.tile p { margin-bottom: 0; font-size: 0.94rem; }

/* Service tiles — one Tabler glyph in a bubble beside the copy.
   The icons are Tabler Icons (MIT), inlined into this page's sprite: one
   family, native 24x24 / 2px / round caps, so stroke weight and density are
   consistent by construction rather than by hand-matching. */
.service-tile {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 17px;
  padding: 22px 24px;
  border: 1px solid rgba(0,0,0,0.07);
  box-shadow: none;
  transition:
    background-color 200ms var(--ease-out),
    border-color 200ms var(--ease-out),
    transform 200ms var(--ease-out),
    box-shadow 200ms var(--ease-out);
}
.service-bubble {
  flex: 0 0 auto;
  width: 50px; height: 50px; border-radius: 15px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-light);
  border: 1px solid rgba(0,0,0,0.07);
  box-shadow: 0 1px 2px rgba(0,0,0,0.025);
  color: #343438;
  transition: background-color 200ms var(--ease-out), border-color 200ms var(--ease-out), color 200ms var(--ease-out);
}
/* Tabler draws at 2px; the site-wide .icon default of 1.6 would thin these
   out and break the family's density. */
.service-bubble .icon { width: 24px; height: 24px; stroke-width: 2; }
/* Real trademarks are filled silhouettes, not stroked outlines. */
.icon-brand { fill: currentColor; stroke: none; }
/* The Apple mark is a solid shape, so at the same box size it reads heavier
   than the outline glyphs beside it. This is the only optical correction in
   the set, and it is a class rather than a per-icon nudge. */
.service-bubble .icon-brand { width: 21px; height: 21px; }
.service-tile > div { min-width: 0; }
.service-tile h3 { font-size: 1rem; margin-bottom: 4px; line-height: 1.25; text-wrap: balance; }
.service-tile p { font-size: 0.88rem; margin: 0; color: var(--text-soft); }

/* Progress sweep along the card's bottom edge. overflow: hidden above keeps
   it inside the rounded corners instead of poking past them. */
.service-tile::after {
  content: ''; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 2px; background: var(--red);
  transform: scaleX(0); transform-origin: left center;
  transition: transform 290ms cubic-bezier(.22,.61,.36,1);
}
.service-tile:has(.tile-link:focus-visible) { background: rgba(230,57,70,0.045); border-color: rgba(230,57,70,0.3); outline: 2px solid var(--red); outline-offset: 3px; }
.service-tile:has(.tile-link:focus-visible)::after { transform: scaleX(1); }
.service-tile:has(.tile-link:focus-visible) .service-bubble { background: rgba(230,57,70,0.08); border-color: rgba(230,57,70,0.22); color: var(--red); }
@media (any-hover: hover) {
  .service-tile:hover {
    background: rgba(230,57,70,0.045);
    border-color: rgba(230,57,70,0.3);
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(11,18,32,0.06);
  }
  .service-tile:hover .service-bubble {
    background: rgba(230,57,70,0.08);
    border-color: rgba(230,57,70,0.22);
    color: var(--red);
  }
  .service-tile:hover::after { transform: scaleX(1); }
}
@media (prefers-reduced-motion: reduce) {
  /* Colour and border feedback stay; the sweep and the lift do not. */
  .service-tile, .service-bubble { transition: background-color 200ms linear, border-color 200ms linear, color 200ms linear; }
  .service-tile:hover { transform: none; }
  .service-tile::after { transition: none; }
}
@media (max-width: 600px) {
  .service-tile { gap: 14px; padding: 18px 18px; }
  .service-bubble { width: 46px; height: 46px; border-radius: 14px; }
  .service-bubble .icon { width: 22px; height: 22px; }
  .service-bubble .icon-brand { width: 19px; height: 19px; }
  /* Each category's services share one surface, divided by hairlines - a list to scan,
     not 33 separate boxes. The red sweep and the tint still answer a press. */
  .grid:has(> .service-tile) {
    gap: 0; background: var(--white); border: 1px solid rgba(0,0,0,0.07);
    border-radius: var(--radius); overflow: hidden;
  }
  .grid:has(> .service-tile) > .service-tile { border: 0; border-radius: 0; padding: 16px 18px; }
  .grid:has(> .service-tile) > .service-tile + .service-tile { border-top: 1px solid rgba(0,0,0,0.07); }
  /* The surface clips overflow, so the keyboard ring is drawn inside the row. */
  .grid:has(> .service-tile) > .service-tile:has(.tile-link:focus-visible) { outline-offset: -3px; }
}
@media (any-pointer: coarse) {
  .service-tile:active { background: rgba(230,57,70,0.045); }
  .service-tile:active::after { transform: scaleX(1); }
}

/* Services intro — "the fault doesn't need a name" beside the "not the limit" note, so
   the first thing under the hero says the catalogue is common repairs, not a menu.
   The note is a still surface, not a .tile: only links react to hover on this site. */
.services-intro { display: grid; gap: 28px; margin-bottom: 64px; }
@media (min-width: 900px) {
  .services-intro { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: 56px; align-items: center; }
}
.services-intro h2 { font-size: clamp(1.9rem, 3.4vw, 2.6rem); text-wrap: balance; margin-bottom: 16px; }
.services-intro__copy p:last-child { margin-bottom: 0; }
.services-note {
  background: var(--paper); border: 1px solid var(--gray-100); border-radius: var(--radius);
  padding: 30px 32px;
}
.services-note p { font-size: 0.98rem; }
.services-note .services-note__lead {
  font-size: 1.15rem; font-weight: 700; line-height: 1.4; letter-spacing: -0.01em;
  color: var(--text); margin-bottom: 12px; text-wrap: balance;
}
.services-note .services-note__symptoms { font-size: 0.9rem; color: var(--gray-500); }
@media (max-width: 600px) { .services-note { padding: 24px 22px; } }
/* Services: a jump list to the eight categories, for screens where the 33 services fall
   into one long column. Desktop shows them two-up and stays as approved, so it is hidden
   there (display: none also keeps it out of the accessibility tree). */
.service-jump { display: none; }
@media (max-width: 860px) {
  .service-jump { display: block; margin: 0 0 44px; }
  .service-jump__label { margin: 0 0 10px; font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
  .service-jump ul { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
  .service-jump a {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    min-height: 48px; padding: 10px 14px; border-radius: var(--radius-sm);
    background: var(--white); border: 1px solid rgba(0,0,0,0.08);
    font-size: 0.88rem; font-weight: 600; line-height: 1.25; color: var(--text); text-decoration: none;
  }
  /* A small chevron pointing down the page: these links move you, they don't leave it. */
  .service-jump a::after {
    content: ""; flex: none; width: 7px; height: 7px; margin-top: -4px;
    border-right: 2px solid var(--gray-450); border-bottom: 2px solid var(--gray-450); transform: rotate(45deg);
  }
  .service-jump a:active { background: var(--surface-light); }
}
@media (max-width: 600px) { .service-jump ul { grid-template-columns: 1fr; } }
@media (max-width: 600px) and (min-width: 20em) { .service-jump ul { grid-template-columns: 1fr 1fr; } }

/* 404 on phones: the heading was the largest of any secondary hero and ran to three lines
   ("turn." alone), and "Back to Home" sat half behind the call bar at 320x568. The two
   buttons share one width; the three way-out links become one list, each row ending in a
   chevron, since on touch nothing else showed they were links. */
@media (max-width: 600px) {
  .page-hero--404 { padding: 44px 0 48px; }
  .page-hero--404 h1 { font-size: clamp(2.2rem, 10.5vw, 2.6rem); line-height: 1.04; text-wrap: balance; margin-bottom: 16px; }
  .page-hero--404 .hero-actions { flex-direction: column; align-items: stretch; gap: 12px; }
}
@media (max-width: 620px) {
  .not-found-links {
    gap: 0; background: var(--white); border: 1px solid var(--gray-100);
    border-radius: var(--radius); box-shadow: var(--shadow-sm); overflow: hidden;
  }
  .not-found-links > .tile { position: relative; padding: 18px 52px 18px 20px; border: 0; border-radius: 0; box-shadow: none; }
  .not-found-links > .tile + .tile { border-top: 1px solid var(--gray-100); }
  .not-found-links > .tile::after {
    content: ""; position: absolute; right: 24px; top: 50%; width: 8px; height: 8px; margin-top: -4px;
    border-top: 2px solid var(--gray-450); border-right: 2px solid var(--gray-450); transform: rotate(45deg);
  }
  .not-found-links > .tile:focus-visible { outline-offset: -4px; }
}
@media (any-pointer: coarse) { .not-found-links > .tile:active { background: var(--surface-light); } }

.brand-strip { display: flex; flex-wrap: wrap; gap: 12px 36px; justify-content: center; align-items: center; }
.brand-word { font-weight: 600; font-size: 1rem; color: var(--gray-500); letter-spacing: -0.01em; }

/* Brand tiers — major brands get a logo badge + click-through mega-panel, minor/legacy brands stay as a small text row */
.brand-major-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 18px; margin-bottom: 36px; }
@media (max-width: 1000px) { .brand-major-grid { grid-template-columns: repeat(4, 1fr); } }

.brand-major-card {
  position: relative;
  /* Shared with script.js, which places the panel against the lifted card. */
  --lift: 6px;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 12px; background: transparent; border: none;
  transition: transform 0.3s ease;
  cursor: pointer;
}
.brand-major-card:focus-visible, .brand-major-card.open {
  transform: translateY(calc(var(--lift) * -1)); z-index: 25;
}
/* Hover only where a hover exists: after a tap, iOS keeps :hover on the card, which left it
   lifted (and its logo enlarged) once the sheet had closed. */
@media (any-hover: hover) {
  .brand-major-card:hover { transform: translateY(calc(var(--lift) * -1)); z-index: 25; }
}

/* Real logos, real brand colour as an accent border — not forced white-on-colour,
   since most of these marks are single-colour and need a light background to read. */
.brand-logo-tile {
  width: 100%; height: 74px; border-radius: 16px;
  background: var(--white); border: 1px solid var(--gray-100);
  border-bottom: 3px solid var(--brand-color, var(--gray-200));
  display: flex; align-items: center; justify-content: center;
  padding: 14px 16px;
  box-shadow: 0 8px 20px -10px rgba(0,0,0,0.25);
  transition: transform 0.35s cubic-bezier(.34,1.56,.64,1), box-shadow 0.3s ease;
}
.brand-logo-tile img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; display: block; }
/* The SVG carries width/height 23, and width: auto kept it there - half the
   size of the HP and Dell marks. Sized explicitly; a solid four-square block
   reads heavier than a ring, so it sits a little under their 42px. */
.tile-surface img { width: 32px; height: 32px; }
.brand-major-card:focus-visible .brand-logo-tile, .brand-major-card.open .brand-logo-tile {
  /* Ambient motion is hover/focus-only now — logos sit still at rest (calmer,
     cheaper to render with many tiles on screen) and only animate as feedback
     for the interaction that actually matters: picking a brand. */
  animation: tile-highlight 1.4s ease-in-out 2 forwards;
  box-shadow: 0 14px 28px -8px var(--brand-color, rgba(0,0,0,0.35));
}
@media (any-hover: hover) {
  .brand-major-card:hover .brand-logo-tile {
    animation: tile-highlight 1.4s ease-in-out 2 forwards;
    box-shadow: 0 14px 28px -8px var(--brand-color, rgba(0,0,0,0.35));
  }
}
@keyframes tile-highlight { 0%, 100% { transform: scale(1.08) translateY(0); } 50% { transform: scale(1.11) translateY(-3px); } }
@media (prefers-reduced-motion: reduce) {
  .brand-major-card:focus-visible .brand-logo-tile, .brand-major-card.open .brand-logo-tile { animation: none; transform: scale(1.08); }
}
@media (prefers-reduced-motion: reduce) and (any-hover: hover) {
  .brand-major-card:hover .brand-logo-tile { animation: none; transform: scale(1.08); }
}

/* Real brand colours, used as the accent border under each logo */
.tile-apple    { --brand-color: #1d1d1f; }
.tile-hp       { --brand-color: #0096D6; }
.tile-dell     { --brand-color: #0076CE; }
.tile-lenovo   { --brand-color: #E2231A; }
.tile-asus     { --brand-color: #0a0a0a; }
.tile-acer     { --brand-color: #83B81A; }
/* border-image silently disables border-radius, which is the whole reason
   this one tile rendered as a square while every other brand stayed
   rounded. It now uses --brand-color like the rest; the Microsoft identity
   lives in the logo, which is where it belongs. */
.tile-surface  { --brand-color: #00A4EF; }
.tile-msi      { --brand-color: #E30613; }
.tile-gigabyte { --brand-color: #FF8200; }
.tile-razer    { --brand-color: #44D62C; }
.tile-sony     { --brand-color: #000000; }
.tile-toshiba  { --brand-color: #ED1C24; }
.tile-lg       { --brand-color: #A50034; }
.tile-samsung  { --brand-color: #1428A0; }

.brand-major-name { font-size: 0.98rem; font-weight: 600; letter-spacing: -0.01em; color: var(--text); text-align: center; }
/* Phones: three to a row with a lighter tile, so fourteen brands are five short rows rather
   than seven tall ones - each tile still a 60px tap target (the sheet opens on tap). Six
   tracks with each card spanning two, so a short last row centres instead of hanging left.
   After the base rules on purpose: placed before them (as they first were) they lost at
   equal specificity and never applied. minmax(0, ...) keeps long names from widening a
   column. */
@media (max-width: 620px) {
  .brand-major-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 6px; }
  .brand-major-card { grid-column: span 2; padding: 6px 4px; gap: 8px; }
  .brand-major-card:nth-last-child(2):nth-child(3n+1) { grid-column: 2 / span 2; }
  .brand-major-card:last-child:nth-child(3n+1) { grid-column: 3 / span 2; }
  .brand-logo-tile { height: 60px; padding: 10px 12px; border-radius: 14px; }
  .brand-major-name { font-size: 0.86rem; }
}
/* Tablets and landscape phones: two rows of seven, like the desktop, instead of four rows
   ending in two orphans (594px for a logo picker). The desktop's 4-column step at
   861-1000px is untouched. */
@media (min-width: 621px) and (max-width: 53.75em) {
  .brand-major-grid { grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 12px; }
  .brand-major-card { padding: 6px 2px; gap: 10px; }
  .brand-logo-tile { height: 64px; padding: 12px; }
  .brand-major-name { font-size: 0.86rem; }
}

/* Click/hover mega-panel — single shared panel, fixed-positioned via JS so it can
   never overlap sibling cards or other page content, and never gets caught in a
   CSS-hover dead zone. Apple-nav inspired: light, roomy, two columns. */
#brand-panel {
  position: fixed; z-index: 500;
  /* A fixed width, not max-content: the two-column family grid then lines up the
     same for a brand with three families as for one with twelve. */
  width: min(460px, calc(100vw - 32px));
  background: var(--white); color: var(--text);
  padding: 24px 26px; border-radius: var(--radius-lg);
  opacity: 0; visibility: hidden; transform: translateY(6px);
  /* visibility: on at once when opening, so focus can move in on the same frame;
     off only after the fade when closing. */
  transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out), visibility 0s linear 180ms, top 0.22s cubic-bezier(.4,0,.2,1), left 0.22s cubic-bezier(.4,0,.2,1);
  box-shadow: var(--shadow-lg); border: 1px solid var(--gray-100); text-align: left;
  pointer-events: none;
}
#brand-panel.open { opacity: 1; visibility: visible; transform: translateY(0); pointer-events: auto; transition-delay: 0s; }

/* Little connector arrow pointing at whichever brand is active */
.panel-arrow {
  position: absolute; width: 16px; height: 16px; background: var(--white);
  transform: rotate(45deg); left: var(--arrow-left, 190px); margin-left: -8px;
}
/* The pointer sits on whichever edge faces the card, so the panel always
   reads as attached to the brand it belongs to rather than floating. */
.panel-side-below .panel-arrow { top: -8px; left: var(--arrow-left, 190px); margin-left: -8px;
  border-top: 1px solid var(--gray-100); border-left: 1px solid var(--gray-100); }
.panel-side-above .panel-arrow { bottom: -8px; left: var(--arrow-left, 190px); margin-left: -8px;
  border-bottom: 1px solid var(--gray-100); border-right: 1px solid var(--gray-100); }
.panel-side-right .panel-arrow { left: -8px; top: var(--arrow-top, 40px); margin-left: 0; margin-top: -8px;
  border-left: 1px solid var(--gray-100); border-bottom: 1px solid var(--gray-100); }
.panel-side-left  .panel-arrow { left: auto; right: -8px; top: var(--arrow-top, 40px); margin-left: 0; margin-top: -8px;
  border-right: 1px solid var(--gray-100); border-top: 1px solid var(--gray-100); }

/* Panel content: the brand and a close button, a two-column grid of model families
   (name plus an optional sub-line people recognise from the machine itself), and a
   footer that keeps unlisted models one tap from an enquiry. */
.panel-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 14px; }
.panel-title { margin: 0; font-size: 1.05rem; font-weight: 700; letter-spacing: -0.01em; color: var(--text); }
.panel-title span { font-weight: 500; color: var(--gray-500); }
.panel-close {
  flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin: -10px -12px -10px 0; padding: 0;
  border: none; border-radius: 50%; background: none; color: var(--gray-500); cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.panel-close:hover, .panel-close:focus-visible { background: var(--gray-50); color: var(--text); }
.panel-close:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }
.panel-close .icon { width: 18px; height: 18px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; }
.panel-families {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px;
}
/* A short window can't fit a long list beside the card: the list scrolls rather than
   the panel growing off-screen (the panel itself can't scroll - that would clip its arrow). */
#brand-panel:not(.is-sheet) .panel-families { max-height: calc(100vh - 210px); overflow-y: auto; }
.panel-family {
  display: block; min-height: 44px; padding: 9px 12px;
  border: 1px solid var(--gray-100); border-radius: 12px; background: var(--white);
  text-decoration: none; transition: border-color 0.15s ease, background-color 0.15s ease;
}
.panel-family__name { display: block; font-size: 0.92rem; font-weight: 600; line-height: 1.3; letter-spacing: -0.01em; color: var(--text); }
.panel-family__note { display: block; margin-top: 2px; font-size: 0.74rem; line-height: 1.35; color: var(--gray-500); }
.panel-family:hover, .panel-family:focus-visible { border-color: rgba(230,57,70,0.35); background: var(--red-tint); }
.panel-family:hover .panel-family__name, .panel-family:focus-visible .panel-family__name { color: var(--red); }
.panel-family:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }
.panel-foot {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--gray-100); font-size: 0.86rem;
}
.panel-ask { display: inline-flex; align-items: center; gap: 5px; font-weight: 600; color: var(--blue); text-decoration: none; }
.panel-ask .icon { width: 14px; height: 14px; }
.panel-ask:hover, .panel-ask:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
.panel-call { display: inline-flex; align-items: center; gap: 6px; font-weight: 500; color: var(--text-soft); text-decoration: none; }
.panel-call .icon { width: 14px; height: 14px; }
.panel-call:hover, .panel-call:focus-visible { color: var(--red); }

/* Sheet mode - set by script for touch taps at any width and for every
   interaction on phones. A finger never hovers, and an anchored panel on a
   tablet would sit over half the brand grid. */
#brand-panel.is-sheet {
  left: 0 !important; right: 0; bottom: 0; top: auto !important; transform: translateY(100%);
  width: auto; min-width: 0; max-width: none; border-radius: 20px 20px 0 0; max-height: 80vh; overflow-y: auto;
  transition: opacity 220ms var(--ease-out), transform 220ms var(--ease-out), visibility 0s linear 220ms;
}
#brand-panel.is-sheet.open { transform: translateY(0); }
/* Clear of the home indicator on phones that have one. */
#brand-panel.is-sheet { padding-bottom: calc(24px + env(safe-area-inset-bottom)); }
#brand-panel.is-sheet .panel-arrow { display: none; }
/* Only the model list scrolls: the heading with its Close button and the "Ask us" / Call
   footer stay on the sheet (on a 568px phone the footer started 340px below the screen and
   Close scrolled away), and a swipe past the end of the list no longer scrolls the page
   underneath; js/script.js also locks the page (html.sheet-open) while the sheet is open.
   Same 24px gutter as the page. */
#brand-panel.is-sheet {
  display: flex; flex-direction: column; overflow: hidden;
  padding-left: max(24px, env(safe-area-inset-left)); padding-right: max(24px, env(safe-area-inset-right));
}
#brand-panel.is-sheet .panel-head, #brand-panel.is-sheet .panel-foot { flex-shrink: 0; }
#brand-panel.is-sheet .panel-families { flex: 0 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
#brand-panel.is-sheet .panel-family__note { font-size: var(--fs-xs); }
@media (min-width: 600px) {
  #brand-panel.is-sheet .panel-families { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
}
html.sheet-open { overflow: hidden; }

.brand-panel-backdrop {
  display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 490;
}
.brand-panel-backdrop.open.is-sheet { display: block; }

.brand-minor-row {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 10px 28px; text-align: center;
  padding-top: 24px; border-top: 1px solid var(--gray-100);
}
.brand-minor-row span { font-size: 0.85rem; color: var(--gray-500); font-weight: 500; }
.brand-minor-label { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--gray-400); margin-right: 4px; }

/* Third-party recognition badges */
.recognition-card { text-align: center; }
.recognition-score { font-size: 2.6rem; font-weight: 600; letter-spacing: -0.02em; color: var(--text); margin-bottom: 2px; font-variant-numeric: tabular-nums; }
.recognition-score small { font-size: 1.1rem; font-weight: 500; color: var(--gray-500); }
.recognition-source { font-weight: 600; font-size: 0.95rem; margin-bottom: 4px; }
.recognition-detail { font-size: 0.85rem; color: var(--gray-500); margin-bottom: 14px; }
/* Phones: four tall centred cards became ~1,150px of scrolling. Each is a row instead - the
   score anchors the left, everything else reads down the right - so all four platforms fit
   on about one screen. Up to 620px, where the 4-up grid goes single-column (at 601-620 they
   fell back to 1,143px of centred cards). */
@media (min-width: 22.5em) and (max-width: 620px) {
  .recognition-card {
    display: grid; grid-template-columns: 104px minmax(0, 1fr); column-gap: 16px;
    align-items: center; text-align: left; padding: 18px 18px 16px;
  }
  .recognition-card > * { grid-column: 2; justify-self: start; }
  .recognition-card .recognition-score { grid-column: 1; grid-row: 1 / span 6; font-size: 2rem; margin: 0; }
  .recognition-card .recognition-score small { font-size: 0.95rem; }
  .recognition-card .recognition-rank { margin-bottom: 6px; }
  .recognition-card .recognition-stars { margin-bottom: 4px; }
  .recognition-card .recognition-detail { margin-bottom: 6px; }
}
/* Below 360px a 104px score column leaves the words too little room: the score and its
   chip share the first line and the rest reads beneath, left-aligned, about 175px a row
   instead of 266-318px centred cards. */
@media (max-width: 22.4375em) {
  .recognition-card {
    display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 12px;
    align-items: center; text-align: left; padding: 18px 16px;
  }
  .recognition-card > * { grid-column: 1 / -1; justify-self: start; }
  .recognition-card .recognition-score { grid-column: 1; grid-row: 1; font-size: 1.9rem; margin: 0; }
  .recognition-card .recognition-rank { grid-column: 2; grid-row: 1; justify-self: end; margin: 0; }
  .recognition-card .recognition-stars { justify-content: flex-start; margin: 2px 0 4px; }
  .recognition-card .recognition-detail { margin-bottom: 6px; }
}
/* ...and the four rows share one surface, divided by hairlines, rather than four separate
   boxes in a column - a list of platforms, not a stack of cards. .grid in the selector:
   the homepage's .home .grid gap (15px) otherwise outranked gap: 0 and spaced the rows
   unevenly around each hairline. */
@media (max-width: 620px) {
  .grid.recognition-grid {
    gap: 0; background: var(--white); border: 1px solid var(--gray-100);
    border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-sm);
  }
  .recognition-grid > .recognition-card { border: 0; border-radius: 0; box-shadow: none; }
  .recognition-grid > .recognition-card + .recognition-card { border-top: 1px solid var(--gray-100); }
  /* The surface clips overflow, so the focus ring is drawn inside each row. */
  .recognition-grid > .recognition-card:focus-visible { outline-offset: -4px; }
}

/* Award seals. The artwork is Three Best Rated's own and their terms forbid
   altering it, so everything here is only the neutral frame around it: a white
   surface (the 2025/2026 badges are white-background PNGs), a hairline border,
   no shadow, no tilt, glow or shine. The seals are the one thing in colour.
   Homepage: the latest award only, beside one line and a link to the history.
   About: all eleven, newest first, each linking to the listing. */
.award-rail {
  display: flex; align-items: center; justify-content: center; gap: 18px 28px;
  margin-top: 18px; padding: 22px 28px;
  background: var(--white); border: 1px solid var(--gray-100); border-radius: var(--radius);
}
.award-rail__badge { flex: none; display: block; border-radius: 50%; transition: transform var(--dur-base) var(--ease-out); }
.award-rail__badge img { width: 112px; height: auto; }
.award-rail__copy { flex: 0 1 560px; }
.award-rail__copy h3 { font-size: 1.1rem; margin-bottom: 4px; }
.award-rail__copy p { font-size: 0.9rem; margin-bottom: 8px; }
.award-rail__links { display: flex; flex-wrap: wrap; gap: 4px 22px; }
.award-rail__links .link-arrow { font-size: 0.88rem; }
@media (max-width: 620px) {
  .award-rail { flex-direction: column; text-align: center; padding: 22px 18px; }
  .award-rail__copy { flex: none; }
  /* Stacked, the two links were 22px targets 4px apart. */
  .award-rail__links { justify-content: center; row-gap: 0; }
  .award-rail__links .link-arrow { min-height: 40px; }
  .award-rail__badge img { width: 96px; }
}

/* Every cell is the same box with the image capped inside it, so the round
   category seals (2016-2024) and the square premium badges (2025-2026) line up
   without either artwork being touched. Six to a row at full width. */
.award-history {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 26px 18px;
  list-style: none; margin: 0 auto; padding: 34px 28px; max-width: 1080px;
  background: var(--white); border: 1px solid var(--gray-100); border-radius: var(--radius);
}
.award-history li { width: 136px; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.award-history a {
  display: flex; align-items: center; justify-content: center; width: 120px; height: 120px;
  border-radius: 50%; transition: transform var(--dur-base) var(--ease-out);
}
.award-history a:focus-visible { outline: 2px solid var(--red); outline-offset: 4px; }
.award-history img { width: auto; height: auto; max-width: 100%; max-height: 120px; }
.award-history__year { font-size: 0.82rem; font-weight: 600; color: var(--gray-500); font-variant-numeric: tabular-nums; }
.award-history__source { text-align: center; font-size: 0.88rem; margin: 18px 0 0; }
.award-history__source .link-arrow { font-size: 0.88rem; }
@media (any-hover: hover) {
  .award-rail__badge:hover,
  .award-history a:hover { transform: translateY(-2px); }
}
@media (max-width: 620px) {
  .award-history { gap: 20px 14px; padding: 26px 16px; }
  .award-history li { width: 92px; }
  .award-history a { width: 88px; height: 88px; }
  .award-history img { max-height: 88px; }
}
/* Testimonials only (the owner's spec for that section): fixed columns - six
   on desktop, three on tablets, two on phones - with flex rather than grid so
   a short last row centres. Placed after the base rules so that, at equal
   specificity, these win over the phone override above. About keeps the
   base layout. */
.award-history--cols { --award-cols: 6; --award-gap: 18px; gap: 26px var(--award-gap); }
.award-history--cols li { width: calc((100% - (var(--award-cols) - 1) * var(--award-gap)) / var(--award-cols)); }
.award-history--cols a { width: min(120px, 100%); height: auto; aspect-ratio: 1; }
.award-history--cols img { max-height: 120px; }
@media (max-width: 1023px) {
  .award-history--cols { --award-cols: 3; }
}
@media (max-width: 620px) {
  .award-history--cols { --award-cols: 2; --award-gap: 14px; gap: 22px var(--award-gap); }
  #awards .section-head p { font-size: 1rem; }
}
/* About below 360px: only two 92px cells fitted a row, so the documented three a row
   became six rows of two. The seals are only shown smaller; the files are untouched. */
@media (max-width: 22.5em) {
  .award-history:not(.award-history--cols) { padding: 22px 10px; gap: 18px 8px; }
  .award-history:not(.award-history--cols) li { width: 76px; }
  .award-history:not(.award-history--cols) a { width: 76px; height: 76px; }
  .award-history:not(.award-history--cols) img { max-height: 76px; }
}
.recognition-rank {
  display: inline-flex; align-items: center; gap: 7px; background: var(--red-tint); color: var(--red-dark);
  font-size: var(--fs-xs); font-weight: 700; padding: 4px 12px 4px 4px; border-radius: var(--pill);
  text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 16px;
}
.rank-icon-chip {
  width: 18px; height: 18px; border-radius: 6px; overflow: hidden; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--white); box-shadow: 0 0 0 1px rgba(0,0,0,0.08);
}
.rank-icon-chip img, .rank-icon-chip svg { width: 100%; height: 100%; object-fit: contain; }
.rank-icon-chip.chip-yp { background: none; }
.rank-icon-chip.chip-yp img { object-fit: cover; }

/* Testimonials */
.testimonial-card { position: relative; }
.review-source-tag {
  display: inline-flex; align-items: center; gap: 7px; font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.05em; padding: 3px 12px 3px 3px; border-radius: var(--pill);
  margin-bottom: 12px;
}
.review-source-tag.google { background: rgba(0,104,201,0.1); color: var(--blue); }
.review-source-tag.yp { background: var(--gray-100); color: var(--text-soft); }
.review-source-tag .icon-circle {
  width: 18px; height: 18px; border-radius: 50%; background: var(--white); overflow: hidden;
  display: flex; align-items: center; justify-content: center; box-shadow: 0 0 0 1px rgba(0,0,0,0.06);
  flex-shrink: 0;
}
.review-source-tag .icon-circle .icon,
.review-source-tag .icon-circle .platform-logo-img { width: 11px; height: 11px; object-fit: contain; }
.review-source-tag.yp .icon-circle .platform-logo-img { width: 100%; height: 100%; object-fit: cover; }

.view-all-reviews {
  display: flex; flex-direction: column; align-items: center; gap: 20px;
  text-align: center; margin-top: 44px; padding: 36px 28px; background: var(--gray-50); border-radius: var(--radius);
}
.view-all-reviews strong { font-size: 1.1rem; }
.review-platform-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 22px; }
.review-platform-link { display: flex; flex-direction: column; align-items: center; gap: 8px; width: 84px; }
/* Testimonials' captions above the ratings and the reviews, and the gap after the ratings. */
.shop-note.ratings-note { text-align: center; margin-bottom: 32px; }
.shop-note.ratings-note--reviews { margin-bottom: 44px; }
.ratings-grid { margin-bottom: 60px; }
/* Phones: the grey panels read as empty blocks between unlabelled stacks, so they become
   captions attached to what follows - 16px to the content, 40px between groups. Four
   84px platform links in a row wrapped every label to four lines; two columns give two. */
@media (max-width: 620px) {
  .shop-note.ratings-note { background: none; padding: 0; margin: 0 0 16px; text-align: left; font-size: var(--fs-sm); }
  .ratings-grid { margin-bottom: 40px; }
  .view-all-reviews { margin-top: 32px; padding: 24px 18px; gap: 16px; }
  .view-all-reviews strong { font-size: 1rem; text-wrap: balance; }
  .review-platform-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px 10px; width: 100%; }
  .review-platform-link { width: auto; }
}
.review-platform-orb {
  width: 52px; height: 52px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-sm); transition: transform 0.25s cubic-bezier(.34,1.56,.64,1), box-shadow 0.25s ease;
  color: #fff;
}
@media (any-hover: hover) { .review-platform-link:hover .review-platform-orb { transform: translateY(-4px) scale(1.08); box-shadow: var(--shadow-md); } }
.review-platform-orb .icon { width: 22px; height: 22px; }
.review-platform-orb .platform-logo-img { width: 28px; height: 28px; object-fit: contain; display: block; }
.review-platform-orb.plat-google, .review-platform-orb.plat-tbr, .review-platform-orb.plat-wheree {
  background: var(--white); box-shadow: var(--shadow-sm), 0 0 0 1px var(--gray-100);
}
.review-platform-orb.plat-yp .platform-logo-img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
.review-platform-link span { font-size: 0.78rem; font-weight: 600; color: var(--text-soft); }
.quote-mark { font-size: 2.6rem; color: var(--red); line-height: 1; font-family: Georgia, serif; margin-bottom: 6px; display: block; }
/* Rating — one component for every star on the site.

   Each star is its own fixed box holding two copies of the same SVG: a
   neutral one underneath and a gold one inside a clipping wrapper whose
   width is set in pixels by script (js/script.js, "Star ratings"). Only the
   visible width of the gold copy changes - never the star itself - so a
   partly filled star is geometrically identical to a full one.

   The fill is calculated from the rating, never typed in: at 18px, 4.9
   gives 18 / 18 / 18 / 18 / 16.2, and 4.8 would give 14.4 on the fifth star
   with no other change. Sizes are fixed per modifier so that arithmetic is
   exact rather than approximate. */
.rating {
  --star-size: 18px;
  --star-gap: 3px;
  display: inline-flex; flex-direction: row; flex-wrap: nowrap;
  align-items: center; gap: var(--star-gap);
  /* Reserve the full row before script fills it, so nothing shifts when it
     does - and so a rating with no script still holds its space. */
  min-width: calc(5 * var(--star-size) + 4 * var(--star-gap));
  height: var(--star-size);
  vertical-align: middle; line-height: 0;
}
.rating--16 { --star-size: 16px; }
.rating--18 { --star-size: 18px; }
.rating--22 { --star-size: 22px; }
.rating-star {
  position: relative;
  width: var(--star-size); height: var(--star-size);
  flex: 0 0 var(--star-size);
}
.rating-star__base,
.rating-star__fill {
  position: absolute; left: 0; top: 0;
  width: var(--star-size); height: var(--star-size);
  /* The site-wide img, svg { max-width: 100%; display: block } would let the
     gold copy shrink to the width of its clipping wrapper - scaling the star
     down instead of clipping it, which is exactly the distortion this
     component exists to avoid. */
  max-width: none;
  fill: currentColor; stroke: none;
}
.rating-star__base { color: rgba(0,0,0,0.15); }
.rating-star__partial {
  position: absolute; left: 0; top: 0;
  height: var(--star-size);
  overflow: hidden;
  color: var(--gold);
}
/* On dark surfaces the unfilled remainder has to lift off the background
   rather than disappear into it. */
.trust-rail .rating-star__base,
.page-hero .rating-star__base,
.section-dark .rating-star__base,
.cta-bubble .rating-star__base { color: rgba(255,255,255,0.22); }
/* Where rating rows sit: beside a figure, under a score, above a quote */
.num-with-rating { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px 12px; }
.trust-cell strong.num-with-rating { display: flex; }
.stat-strip strong.num-with-rating { display: inline-flex; }
.recognition-stars { display: flex; justify-content: center; margin: 4px 0 12px; }
/* Block-level so the row doesn't pick up the paragraph's line-height strut. */
.testimonial-card .rating { display: flex; width: max-content; margin-bottom: 14px; }
/* Phones: the quote mark had its own 42px line between the source tag and the stars, so
   the words started 148px into the card. It is decoration, so it moves to the corner and
   the review starts sooner, in a slightly wider column. Homepage rail and Testimonials. */
@media (max-width: 620px) {
  .reviews-grid { gap: 14px; }
  .tile.testimonial-card { padding: 22px 20px 24px; }
  .testimonial-card .quote-mark { position: absolute; top: 14px; right: 18px; margin: 0; font-size: 2.4rem; }
  .testimonial-card .rating { margin-bottom: 12px; }
  .testimonial-card .testimonial-author { margin-top: 14px; }
}
/* Homepage reviews on phones: six full reviews stacked ran past 2,000px. They become a row
   to swipe through - each card most of the screen wide, the next one peeking in so the row
   reads as more to see. Native scrolling and snap only; no script moves anything. Every
   review stays whole, and all of them are on the Testimonials page too. js/script.js makes
   the row a labelled, keyboard-scrollable region only while it actually scrolls. */
@media (max-width: 600px) {
  .review-rail {
    display: flex; gap: 12px; overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory; scroll-padding-inline: 24px;
    margin-inline: -24px; padding: 4px 24px 14px;
    scrollbar-width: none;
  }
  .review-rail::-webkit-scrollbar { display: none; }
  .review-rail > .testimonial-card { flex: 0 0 86%; scroll-snap-align: start; }
}
.testimonial-quote { color: var(--text); font-size: 1.02rem; }
.testimonial-author { display: flex; align-items: center; gap: 10px; margin-top: 18px; font-weight: 600; font-size: 0.9rem; color: var(--text); }
.avatar-circle {
  flex: none; width: 36px; height: 36px; border-radius: 50%; background: var(--navy);
  color: var(--white); display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 0.85rem;
}

/* CTA band — dark glow, shorter hero */
/* CTA — a contained surface inside the page, not a full-bleed coloured
   rectangle. The section itself is now transparent so the page background
   runs straight through, and the dark belongs to the bubble. */
.cta-band {
  position: relative; z-index: 2;
  /* The section above already ends in its own padding; adding 56px on top
     of that left a 120px void before every closing CTA. */
  padding: 0 0 64px; text-align: center;
}
.cta-band--inline { padding: 0; }
.cta-band .container { position: relative; z-index: 1; }
.cta-bubble {
  position: relative; overflow: hidden;
  background: #101114;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 30px;
  padding: 56px 48px;
  color: var(--white);
}
/* One ambient red light, sitting under the primary action rather than
   washing the whole surface. Depth first, gradient second. */
.cta-bubble::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse 58% 85% at 50% 116%, rgba(230,57,70,0.16), transparent 70%);
}
.cta-bubble > * { position: relative; z-index: 1; }
.cta-bubble h2 { color: var(--white); }
.cta-bubble p { color: rgba(255,255,255,0.7); }
/* The centred band's paragraph otherwise runs the full 1080px as one line;
   cap the measure so it reads as a sentence, not a strip. */
.cta-bubble:not(.cta-bubble--split) > p { max-width: 58ch; margin-inline: auto; }
/* Mid-page variant: copy left, one action right, inside the same surface. */
@media (min-width: 761px) {
  .cta-bubble--split {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
    gap: 40px; text-align: left; padding: 44px 52px;
  }
  .cta-bubble--split h2 { margin-bottom: 8px; }
  .cta-bubble--split p { margin: 0; max-width: 64ch; }
}
.cta-bubble--split .eyebrow { margin-bottom: 10px; }
@media (max-width: 760px) { .cta-bubble--split p { margin-bottom: 24px; } }
.cta-bubble .btn { white-space: normal; max-width: 100%; }
.cta-bubble h2, .legal-doc a, .legal-contact a, .footer-grid a { overflow-wrap: anywhere; }
@media (max-width: 720px) {
  .cta-band { padding: 0 0 44px; }
  .cta-band--inline { padding: 0; }
  .cta-bubble { padding: 34px 24px; border-radius: 26px; }
}
/* Phones: the closing band's stacked buttons were their own widths (217px and 184px),
   centred, so the pair read ragged; one shared width. The mid-page split band keeps its
   single button. */
@media (max-width: 620px) {
  .cta-bubble:not(.cta-bubble--split) .hero-actions { flex-direction: column; align-items: stretch; gap: 12px; }
}
@media (max-width: 400px) { .cta-bubble { padding: 30px 18px; } }
/* Services: the closing "not listed?" panel - call, enquire, or the shop's address.
   The closing bands elsewhere end with the same walk-in line (.cta-visit). */
.services-unlisted .services-unlisted__visit, .cta-bubble .cta-visit { margin: 22px auto 0; font-size: 0.95rem; }
/* inline-block: the global `svg { display: block }` otherwise lifts the pin onto a line of its own. */
.services-unlisted__visit .icon, .cta-visit .icon { display: inline-block; width: 16px; height: 16px; vertical-align: -3px; margin-right: 2px; color: var(--red); }
.services-unlisted__visit a, .cta-visit a {
  color: var(--white); text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: rgba(255,255,255,0.4);
}
@media (any-hover: hover) { .services-unlisted__visit a:hover, .cta-visit a:hover { text-decoration-color: var(--white); } }

/* Product / shop tiles */
.product-tile { display: flex; flex-direction: column; transition: opacity 200ms ease, transform 200ms ease; }
.product-media {
  aspect-ratio: 4/3; border-radius: var(--radius-sm); background: var(--gray-50);
  display: flex; align-items: center; justify-content: center; color: var(--gray-300); margin-bottom: 18px;
  overflow: hidden;
}
.product-media img { width: 100%; height: 100%; object-fit: cover; }
.product-tile h3 { line-height: 1.25; }
.product-media .icon { width: 42px; height: 42px; }
.product-tag {
  align-self: flex-start; background: var(--navy); color: var(--white); font-size: var(--fs-xs);
  font-weight: 600; padding: 4px 11px; border-radius: var(--pill); margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.04em;
}
.product-tag.used { background: var(--gray-500); }
.product-tile h3 { margin-bottom: 4px; font-size: 1.05rem; }
.product-price { font-size: 1.35rem; font-weight: 600; color: var(--text); margin: 8px 0 18px; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.product-actions { display: flex; gap: 10px; margin-top: auto; }
.product-actions .btn { flex: 1; padding: 11px 14px; font-size: 0.85rem; }
.shop-filters { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 40px; justify-content: center; }
.filter-btn {
  background: var(--white); border: 1px solid var(--gray-300); border-radius: var(--pill);
  padding: 9px 20px; font-family: inherit; font-weight: 500; font-size: 0.85rem; cursor: pointer; color: var(--text-soft);
  transition: border-color 0.15s ease, color 0.15s ease;
}
.filter-btn.active { border-color: var(--red); color: var(--red-dark); }
@media (any-hover: hover) { .filter-btn:hover { border-color: var(--red); color: var(--red-dark); } }
.shop-note { background: var(--gray-50); border-radius: var(--radius); padding: 22px 26px; margin-bottom: 40px; font-size: 0.9rem; color: var(--text-soft); }

/* Shop catalogue - real stock only. While the product grid holds no
   .product-tile, the page shows .shop-empty instead, and the filters and the
   fit note (which only make sense with items to act on) step aside. Done in
   CSS rather than script so it holds with script off and there is no class
   to forget: the first real product tile is what switches the page over. */
.shop-catalogue:not(:has(.product-tile)) .shop-filters,
.shop-catalogue:not(:has(.product-tile)) .product-grid,
.shop-catalogue:not(:has(.product-tile)) .shop-fit-note { display: none; }
.shop-catalogue:has(.product-tile) .shop-empty { display: none; }
/* Browsers without :has() (Chrome < 105, Safari < 15.4, Firefox < 121) drop
   both rules above whole, which would show dead filter buttons beside the
   empty panel. There, script.js marks the catalogue .has-products instead. */
@supports not selector(:has(*)) {
  .shop-catalogue:not(.has-products) .shop-filters,
  .shop-catalogue:not(.has-products) .shop-fit-note { display: none; }
  .shop-catalogue.has-products .shop-empty { display: none; }
}
.shop-empty {
  max-width: 640px; margin: 0 auto; padding: 44px 32px; text-align: center;
  background: var(--white); border: 1px solid var(--gray-100); border-radius: var(--radius-lg);
}
.shop-empty__badge {
  width: 50px; height: 50px; border-radius: 14px; margin: 0 auto 20px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(11,18,32,0.06); color: var(--navy);
}
.shop-empty__badge .icon { width: 24px; height: 24px; }
.shop-empty h2 { font-size: clamp(1.6rem, 3vw, 2.1rem); text-wrap: balance; }
.shop-empty p { max-width: 46ch; margin: 0 auto 26px; }
.shop-empty__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }
@media (max-width: 620px) {
  .shop-empty { padding: 34px 20px; }
  .shop-empty__actions .btn { flex: 1 1 100%; }
}

/* Forms */
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.form-grid .full { grid-column: 1 / -1; }
@media (max-width: 620px) { .form-grid { grid-template-columns: 1fr; } }
label { display: block; font-weight: 500; font-size: 0.85rem; color: var(--text); margin-bottom: 6px; }
/* 16px text: smaller and iOS Safari zooms the page when a field is focused.
   --gray-450 border: 3.6:1 against white, so the field's edge meets WCAG 1.4.11. */
input, textarea, select {
  width: 100%; padding: 13px 15px; border: 1px solid var(--gray-450); border-radius: var(--radius-sm);
  font-family: inherit; font-size: 1rem; color: var(--text); background: var(--white);
}
input[aria-invalid="true"], textarea[aria-invalid="true"], select[aria-invalid="true"] { border-color: var(--error); box-shadow: 0 0 0 1px var(--error); }
.field-hint { margin: 6px 0 0; font-size: var(--fs-sm); color: var(--gray-500); }
.field-error { margin: 6px 0 0; font-size: var(--fs-sm); font-weight: 600; color: var(--error); }
.field-error:empty { display: none; }
.contact-form-tile { max-width: 680px; margin: 0 auto; padding: 40px; }
/* The form's own title, a step down from the section heading above the steps, so the
   page doesn't stack two large headings. */
.contact-form-tile__title { font-size: 1.5rem; letter-spacing: -0.02em; margin: 0 0 6px; }
/* Prefilled links land here, on the form, rather than on the section above it. */
#contact-form .form-about { margin: 0 0 18px; }
/* Phones: the form sits on the page, on the same 24px edge as the heading and steps above
   it, under a hairline - not boxed inside a card inside the section, which made its fields
   38px narrower than everything else. */
@media (max-width: 620px) {
  .contact-form-tile { padding: 28px 0 0; border: 0; border-top: 1px solid var(--gray-200); border-radius: 0; box-shadow: none; background: none; }
}
/* The privacy note under Send is centred under the wider form; on phones it is left-aligned
   with every other line of the form. */
@media (min-width: 621px) { .form-note--legal { text-align: center; } }
/* Contact: how it works. The four steps are one sequence - problem, quote, approval,
   warranty - so they sit on one rail rather than in four boxes: a vertical line on phones
   and tablets, a horizontal one from 64em (1024px), where four columns have room to read
   (at 960px the first title already broke onto two lines). Each marker is a small ring
   and each segment of rail runs from one marker to the next. The numbers are hidden from
   assistive technology, which already announces the position. */
.process { margin-bottom: 40px; }
.process__steps {
  --step-gap: 22px;
  /* 600px: the width of the form's fields below (680px card less its padding), so the
     steps and the form share one left edge. */
  list-style: none; margin: 0 auto; padding: 0; max-width: 600px;
  display: grid; gap: var(--step-gap);
}
/* Phones and tablets: number and title share the first line, the explanation runs under
   both - one row less per step than stacking them, so the list stays short. */
.process-step {
  position: relative; padding-left: 34px;
  display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 10px; align-items: baseline;
}
.process-step::before {
  content: ""; position: absolute; left: 0; top: 6px; width: 12px; height: 12px;
  border-radius: 50%; background: var(--white); border: 2px solid var(--red);
}
.process-step:not(:last-child)::after {
  content: ""; position: absolute; left: 5.5px; top: 24px; bottom: calc(2px - var(--step-gap));
  width: 1px; background: var(--gray-300); transform-origin: top;
}
.process-step__num {
  display: block; font-size: 0.8125rem; line-height: 22px; font-weight: 700; letter-spacing: 0.02em;
  color: var(--red-dark); font-variant-numeric: tabular-nums;
}
.process-step__title { margin: 0; font-size: 1.0625rem; line-height: 24px; letter-spacing: -0.01em; color: var(--text); text-wrap: balance; }
.process-step__text { grid-column: 1 / -1; margin: 4px 0 0; font-size: 0.95rem; line-height: 1.5; color: var(--text-soft); max-width: 40ch; }
.process-step__text a { color: var(--text); font-weight: 600; text-decoration: underline; text-decoration-color: var(--gray-300); text-underline-offset: 3px; }
@media (min-width: 64em) {
  .process { margin-bottom: 56px; }
  .process__head { margin-bottom: 44px; }
  .process__steps { --step-gap: 40px; max-width: 1040px; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0 var(--step-gap); }
  /* Desktop: number, title and explanation stacked under the rail. */
  .process-step { display: block; padding: 34px 0 0; }
  .process-step::before { top: 0; }
  .process-step__title { margin: 2px 0 4px; font-size: 1.125rem; line-height: 1.3; }
  .process-step__text { margin: 0; line-height: 1.55; }
  /* From 8px after this marker to 8px before the next one, across the column gap. */
  .process-step:not(:last-child)::after {
    left: 20px; top: 5.5px; bottom: auto; width: calc(100% + var(--step-gap) - 28px); height: 1px;
    transform-origin: left;
  }
  .process-step__text { max-width: 30ch; }
}
/* A pointer resting on a step marks it - the ring fills - without moving anything. */
@media (any-hover: hover) {
  .process-step:hover::before { background: var(--red); }
  .process-step:hover .process-step__text { color: var(--text); }
}
/* The one entrance on this page, played once: the heading settles, then the rail draws
   from 01 to 04 and each step arrives as the line reaches it - about a second in all.
   js/script.js adds .process--animate only where motion is wanted and the browser can
   observe, then .is-visible when the block is on screen; without it nothing is hidden. */
.process-step:nth-child(2) { --i: 1; }
.process-step:nth-child(3) { --i: 2; }
.process-step:nth-child(4) { --i: 3; }
.process--animate .process__head { transition: opacity 500ms var(--ease-out), transform 500ms var(--ease-out); }
.process--animate .process-step::before {
  transition: opacity 300ms var(--ease-out) calc(150ms + var(--i, 0) * 120ms),
              transform 300ms var(--ease-out) calc(150ms + var(--i, 0) * 120ms),
              background-color var(--dur-base) var(--ease-out);
}
.process--animate .process-step::after { transition: transform 380ms var(--ease-out) calc(230ms + var(--i, 0) * 120ms); }
.process--animate :is(.process-step__num, .process-step__title) {
  transition: opacity 420ms var(--ease-out) calc(200ms + var(--i, 0) * 120ms),
              transform 420ms var(--ease-out) calc(200ms + var(--i, 0) * 120ms);
}
.process--animate .process-step__text {
  transition: opacity 420ms var(--ease-out) calc(260ms + var(--i, 0) * 120ms),
              transform 420ms var(--ease-out) calc(260ms + var(--i, 0) * 120ms),
              color var(--dur-base) var(--ease-out);
}
.process--animate:not(.is-visible) .process__head { opacity: 0; transform: translateY(14px); }
.process--animate:not(.is-visible) .process-step::before { opacity: 0; transform: scale(0.4); }
.process--animate:not(.is-visible) .process-step::after { transform: scaleY(0); }
.process--animate:not(.is-visible) :is(.process-step__num, .process-step__title, .process-step__text) { opacity: 0; transform: translateY(8px); }
@media (min-width: 64em) {
  .process--animate:not(.is-visible) .process-step::after { transform: scaleX(0); }
}
@media print {
  .process--animate :is(.process__head, .process-step__num, .process-step__title, .process-step__text),
  .process--animate .process-step::before, .process--animate .process-step::after { opacity: 1 !important; transform: none !important; }
}
/* Contact hero: the number and hours, for people who came here only to find them. */
/* No ch-based cap like the paragraph above it: a ch is a different width in the fallback
   font and in Manrope, so the line wrapped differently before and after the font arrived
   and the whole hero copy jumped (CLS 0.16 on desktop). */
.page-hero .page-hero__quick { margin-top: 14px; font-size: 1rem; color: rgba(255,255,255,0.82); max-width: none; }
.page-hero__quick a { color: var(--white); font-weight: 700; text-decoration: underline; text-underline-offset: 3px; text-decoration-color: rgba(255,255,255,0.4); }
input:focus, textarea:focus, select:focus { outline: 2px solid var(--red); outline-offset: 1px; border-color: var(--red); }
textarea { resize: vertical; min-height: 130px; }
.form-note { font-size: 0.8rem; color: var(--gray-500); margin-top: 10px; }
/* The privacy link under the contact form: underlined, since grey-on-grey alone wouldn't mark it as a link. */
#contact-form .form-note a { color: var(--text-soft); text-decoration: underline; text-underline-offset: 2px; }
@media (any-hover: hover) { #contact-form .form-note a:hover { color: var(--red); } }
/* What the visitor picked (a service tile, the brand picker, the Shop), shown above the
   message box rather than written into it. No display value here, so [hidden] still hides it. */
.form-about {
  margin: 0 0 12px; padding: 10px 14px; border-radius: var(--radius-sm);
  background: var(--paper); border: 1px solid var(--gray-100);
  font-size: 0.9rem; color: var(--text);
}
.form-success { display: none; background: var(--success-tint); border: 1px solid var(--success-line); color: var(--success); padding: 14px 18px; border-radius: var(--radius-sm); margin-bottom: 18px; font-weight: 600; font-size: 0.95rem; }
.form-success.form-status--error { background: var(--error-tint); border-color: var(--error-line); color: var(--error); }
.form-success a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.form-success:focus { outline: 2px solid currentColor; outline-offset: 3px; }
.form-success.visible { display: block; animation: form-success-in 400ms ease-out; }
@keyframes form-success-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) { .form-success.visible { animation: none; } }

/* Visit-us info — clean label + value, no icon badge */
.visit-info-row { display: flex; flex-wrap: wrap; gap: 40px; justify-content: center; margin-bottom: 36px; }
.visit-info-item { text-align: center; }
.visit-info-label {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  font-size: 0.76rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--red-dark); margin-bottom: 10px;
}
.visit-info-label .icon { width: 15px; height: 15px; }
.visit-info-value { font-size: 1.1rem; font-weight: 600; color: var(--text); line-height: 1.5; }

.map-frame { border-radius: var(--radius-lg); overflow: hidden; border: 1px solid var(--gray-100); box-shadow: var(--shadow-sm); }
.map-frame iframe { width: 100%; height: 440px; border: 0; display: block; }
@media (max-width: 640px) { .map-frame iframe { height: 320px; } }

/* Contact method cards — the primary "how to reach us" row */
.contact-method-card { display: block; text-align: center; padding: 24px 20px; text-decoration: none; }
.contact-method-icon {
  position: relative; width: 50px; height: 50px; border-radius: 50%; margin: 0 auto 14px;
  display: flex; align-items: center; justify-content: center; color: #fff;
}
.contact-method-icon .icon { width: 20px; height: 20px; }
.contact-method-icon::after {
  content: ""; position: absolute; inset: -5px; border-radius: 50%; border: 2px solid currentColor;
  opacity: 0; animation: contact-pulse 2.6s ease-out 1;
}
.contact-method-card:nth-child(2) .contact-method-icon::after { animation-delay: 0.5s; }
.contact-method-card:nth-child(3) .contact-method-icon::after { animation-delay: 1s; }
@keyframes contact-pulse {
  0% { transform: scale(0.9); opacity: 0.5; }
  70% { transform: scale(1.4); opacity: 0; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .contact-method-icon::after { animation: none; display: none; } }
.icon-tone-red { background: var(--red); }
.icon-tone-navy { background: var(--navy); }
.icon-tone-blue { background: var(--blue); }
.contact-method-card h3 { font-size: 0.86rem; margin-bottom: 6px; color: var(--gray-500); font-weight: 500; }
.contact-method-value { font-size: 1.02rem; font-weight: 700; color: var(--text); margin-bottom: 8px; transition: color 0.15s ease; }
@media (any-hover: hover) { .contact-method-card:hover .contact-method-value { color: var(--red); } }
.contact-method-tag {
  display: inline-block; font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--gray-500); background: var(--gray-50); padding: 3px 10px; border-radius: 999px;
}
/* Phones: three tall centred cards took most of a screen for three lines of contact
   detail. They become one list, icon left, divided by hairlines - the homepage platform
   list's pattern - and each whole row still dials or mails. */
@media (max-width: 620px) {
  .contact-methods {
    gap: 0; background: var(--white); border: 1px solid var(--gray-100);
    border-radius: var(--radius); box-shadow: var(--shadow-sm); overflow: hidden;
  }
  .contact-methods > .contact-method-card {
    display: grid; grid-template-columns: 44px minmax(0, 1fr); column-gap: 16px; align-items: center;
    text-align: left; padding: 16px 18px; border: 0; border-radius: 0; box-shadow: none;
  }
  .contact-methods > .contact-method-card + .contact-method-card { border-top: 1px solid var(--gray-100); }
  .contact-methods .contact-method-icon { grid-row: 1 / span 3; width: 44px; height: 44px; margin: 0; }
  .contact-methods .contact-method-card > :not(.contact-method-icon) { grid-column: 2; }
  .contact-methods .contact-method-card h3 { margin-bottom: 2px; }
  .contact-methods .contact-method-value { margin-bottom: 6px; }
  .contact-methods .contact-method-tag { justify-self: start; }
  .contact-methods > .contact-method-card:focus-visible { outline-offset: -4px; }
}
@media (any-pointer: coarse) {
  .contact-method-card:active .contact-method-value { color: var(--red-dark); }
}
/* The address opens the map (the top bar's link is hidden on phones). */
.visit-info-value a { color: inherit; }
/* Phones: each pair of hours on its own line - the row used to break "Sat:" away from its
   times - and the address and hours closer together, as one block. */
@media (max-width: 620px) {
  .visit-info-row { gap: 22px; margin-bottom: 28px; }
  .visit-info-value .nowrap { display: block; }
  .visit-sep { display: none; }
  .visit-info-value a { text-decoration: underline; text-decoration-color: var(--gray-300); text-underline-offset: 3px; }
}

/* Footer — dense, Apple-style */
/* The bottom padding clears the fixed back-to-top button, so the last footer line (the
   independence statement) is never under it at full scroll. Phones have no such button
   (the body's own padding clears the call bar). */
.site-footer { background: var(--gray-50); color: var(--text-soft); padding: 60px 0 92px; border-top: 1px solid var(--gray-100); }
@media (max-width: 620px) { .site-footer { padding-bottom: 32px; } }
/* Five columns once Legal joined; below 1080px the brand blurb takes its own row so the
   four link columns keep room for "Returns, Refunds & Warranty" and the email address. */
.footer-grid { display: grid; grid-template-columns: 1.3fr 0.9fr 0.95fr 1.25fr 1.2fr; gap: 32px; margin-bottom: 40px; }
@media (max-width: 1079px) { .footer-grid { grid-template-columns: repeat(4, 1fr); } .footer-grid > :first-child { grid-column: 1 / -1; } }
@media (max-width: 780px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
/* Phones: Company and Services side by side (short links), with the blurb, Contact (the
   address is the longest line) and Legal (long page names) across both columns. */
@media (max-width: 480px) {
  .footer-grid { grid-template-columns: 1fr 1fr; gap: 28px 20px; }
  .footer-grid > :first-child, .footer-grid > :nth-child(n+4) { grid-column: 1 / -1; }
}
.site-footer .footer-heading { color: var(--text); font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 16px; font-weight: 600; }
.site-footer .logo { color: var(--text); }
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; font-size: 0.88rem; }
@media (any-hover: hover) { .site-footer a:hover { color: var(--red); } }
/* Phones: 19px links on a 32px pitch passed 2.5.8 only on spacing; each row is now a 36px
   target, the blurb stops being the largest text in the footer, and the lines that dial or
   mail read as links beside the plain address and hours. */
@media (max-width: 620px) {
  .site-footer ul { gap: 0; }
  .site-footer li { min-height: 36px; display: flex; align-items: center; }
  .site-footer ul a { display: flex; align-items: center; min-height: 36px; }
  .footer-grid > :first-child p { font-size: 1rem; }
  .site-footer a[href^="tel:"], .site-footer a[href^="mailto:"] { color: var(--text); font-weight: 600; }
  .social-row a { width: 44px; height: 44px; }
}
.footer-bottom {
  border-top: 1px solid var(--gray-100); padding-top: 20px; display: flex; justify-content: space-between;
  flex-wrap: wrap; gap: 10px; font-size: 0.8rem; color: var(--gray-500);
}
.footer-bottom .footer-tm { max-width: 64ch; }
.social-row { display: flex; gap: 12px; margin-top: 14px; }
.social-row a {
  width: 34px; height: 34px; border-radius: 50%; background: var(--gray-100); color: var(--text-soft);
  display: flex; align-items: center; justify-content: center; transition: background-color 0.15s ease, color 0.15s ease;
}
.social-row .icon { width: 16px; height: 16px; }
@media (any-hover: hover) { .social-row a:hover { background: var(--red); color: var(--white); } }

/* Sticky mobile call bar */
.mobile-call-bar {
  display: none; position: fixed; bottom: 0; left: 0; right: 0; z-index: 95;
  background: rgba(10,10,12,0.9); backdrop-filter: blur(16px);
  padding: 10px 16px; gap: 10px;
  box-shadow: 0 -4px 16px rgba(0,0,0,0.2);
}
/* One height for the bar, the page's bottom padding and the scroll padding (the body
   reserved 62px under a 76px bar, so 14px of every page sat under it), plus the iPhone
   home-indicator inset, so the buttons are never under the swipe bar. 48px buttons. */
@media (max-width: 620px) {
  .mobile-call-bar {
    display: flex; align-items: stretch;
    height: calc(var(--callbar-h) + env(safe-area-inset-bottom));
    padding: 8px max(16px, env(safe-area-inset-right)) calc(8px + env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  }
  .mobile-call-bar .btn { padding-block: 0; }
  body { padding-bottom: calc(var(--callbar-h) + env(safe-area-inset-bottom)); }
  /* Keyboard focus scrolled to the bottom edge would otherwise sit under the fixed call bar. */
  html { scroll-padding-bottom: calc(var(--callbar-h) + 24px); }
}
.mobile-call-bar .btn { flex: 1; min-width: 0; white-space: normal; }
/* While a section's own Call / Get a Free Quote pair is on screen (closing bands, the
   empty Shop panel), the bar steps down out of the way: three red Call buttons in one view
   was noise, and at 320px the bar sat over the section's second button. js/script.js sets
   the class; visibility keeps the hidden bar's links out of the tab order. */
.mobile-call-bar { transition: transform var(--dur-base) var(--ease-out), visibility 0s; }
.mobile-call-bar.is-redundant { transform: translateY(100%); visibility: hidden; transition: transform var(--dur-base) var(--ease-out), visibility 0s linear var(--dur-base); }
/* The secondary action on the dark bar (was an inline style on every page). */
.mobile-call-bar .btn-secondary { color: var(--white); border-color: rgba(255,255,255,0.35); }

.skip-link {
  position: absolute; left: -999px; top: 0; background: var(--red-dark); color: var(--white);
  padding: 10px 16px; z-index: 200; border-radius: 0 0 8px 0;
}
.skip-link:focus { left: 0; }

/* Back-to-top — a scroll-progress ring, not just a plain arrow button */
.back-to-top {
  /* z-index 99: above the mobile call bar (95), below the header (100) so the open drawer covers it */
  position: fixed; right: 24px; bottom: 24px; z-index: 99;
  width: 52px; height: 52px; border-radius: 50%;
  background: var(--white); border: none; cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-md);
  opacity: 0; visibility: hidden; transform: translateY(14px) scale(0.9);
  transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s, box-shadow 0.2s ease;
}
.back-to-top.visible { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }
@media (any-hover: hover) {
  .back-to-top:hover { box-shadow: var(--shadow-lg); transform: translateY(-3px) scale(1.05); }
}
.back-to-top-ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.back-to-top-ring .ring-track { fill: none; stroke: var(--gray-100); stroke-width: 2.5; }
.back-to-top-ring .ring-progress {
  fill: none; stroke: var(--red); stroke-width: 2.5; stroke-linecap: round;
  stroke-dasharray: 119.4; stroke-dashoffset: 119.4; transition: stroke-dashoffset 0.1s linear;
}
.back-to-top .icon { position: relative; width: 18px; height: 18px; stroke-width: 2.4; color: var(--navy); transition: color 0.2s ease; }
@media (any-hover: hover) { .back-to-top:hover .icon { color: var(--red); } }
/* Clear of the notch and the home indicator on landscape iPhones (insets are 0 elsewhere). */
.back-to-top { right: max(24px, env(safe-area-inset-right)); bottom: max(24px, env(safe-area-inset-bottom)); }
/* Phones: off. Floating above the call bar it sat over the right end of form fields,
   running text and tap targets on every page; the sticky header is always one tap away. */
@media (max-width: 620px) { .back-to-top { display: none; } }
@media (prefers-reduced-motion: reduce) { .back-to-top { transition: opacity 0.25s ease, visibility 0.25s; } }
