@charset "UTF-8";
/* ==========================================================================
   SPINWELL APPLIANCE CARE — SITE STYLESHEET
   ==========================================================================

   Design direction: "PRECISION UTILITY"
   ------------------------------------------------------------------------
   The visual language of technical documentation and calibrated instruments,
   not lifestyle photography. The person reading this has a broken washing
   machine and needs to believe we are precise, honest and fast.

   Deliberate anti-template decisions (each one is a choice, not an accident):
     1. NO soft drop shadows anywhere. Elevation comes from hairlines and
        offset "print register" edges. Soft shadows are the #1 cheap-template
        tell in the local-services category.
     2. Small radii (3-8px), never pill-shaped cards.
     3. Headlines use Archivo's WIDTH axis compressed to 68-78. Condensed
        grotesque at scale reads like equipment labelling / industrial signage.
     4. Mono data labels with section numbering (§01, §02) - spec-sheet feel.
     5. Corner crop-marks on key panels instead of universal rounding.
     6. Diagonal hatch fills instead of gradients.
     7. Tabular numerals everywhere a number appears, so figures never jitter.
     8. Oxide (the warm red) is rationed: CTAs and one accent per section.
        Never decorative.

   ALL colour values below were verified with a real WCAG contrast
   calculation. Ratios are recorded in comments. Do not change a colour
   without re-checking the pair it is used against.

   ------------------------------------------------------------------------
   FILE MAP
     01  Fonts
     02  Design tokens
     03  Reset & base
     04  Typography
     05  Layout primitives
     06  Motion primitives & scroll reveals
     07  Components
     08  Utilities
     09  Reduced motion / forced colours / print
   ========================================================================== */


/* ==========================================================================
   01  FONTS
   --------------------------------------------------------------------------
   Self-hosted, custom-subset. 84 KB total for three families.

   The rupee sign U+20B9 is included in the PRIMARY subset of every family.
   This matters: Google Fonts DECLARES U+20AD-20C0 in its latin-ext
   unicode-range, but several popular faces (Instrument Sans, Outfit,
   JetBrains Mono, Roboto Mono) contain no rupee glyph at all. Prices would
   silently fall back to another typeface. Verified present here.
   ========================================================================== */

/* Display — Archivo. wght 500-800, wdth 62-100. The width axis is the idea. */
@font-face {
  font-family: "Archivo VF";
  src: url("../fonts/archivo-var.woff2") format("woff2-variations");
  font-weight: 500 800;
  font-stretch: 62% 100%;
  font-style: normal;
  font-display: swap;
}

/* Body / UI — Geist. */
@font-face {
  font-family: "Geist VF";
  src: url("../fonts/geist-var.woff2") format("woff2-variations");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

/* Data labels — Geist Mono, single weight. */
@font-face {
  font-family: "Geist Mono VF";
  src: url("../fonts/geist-mono-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}


/* ==========================================================================
   02  DESIGN TOKENS
   ========================================================================== */

:root {
  /* ---- SURFACES ------------------------------------------------------- */
  --paper:        #F4F1EA;   /* page background. Warm, not cold grey.       */
  --paper-2:      #EBE7DD;   /* inset panels                                */
  --paper-3:      #E2DDD1;   /* deeper inset / table stripes                */
  --surface:      #FFFFFF;   /* cards that must lift off the paper          */
  --petrol:       #0A3A3C;   /* dark sections                               */
  --petrol-2:     #0E4B4D;   /* elevated surface on dark                    */
  --petrol-3:     #062A2B;   /* deepest well                                */
  --ink:          #0F1416;   /* near-black, for the darkest panels          */

  /* ---- TEXT ----------------------------------------------------------- */
  --text:         #0F1416;   /* 16.45:1 on paper  AAA                       */
  --text-2:       #3F4649;   /*  8.53:1 on paper  AAA  secondary            */
  --text-3:       #5A6266;   /*  5.52:1 on paper  AA   labels/meta          */
  --text-inv:     #F4F1EA;   /* 11.06:1 on petrol AAA                       */
  --text-inv-2:   #B4C6C6;   /* muted text on petrol                        */
  --text-on-cta:  #FFFFFF;   /*  5.61:1 on oxide  AA                        */

  /* ---- BRAND ---------------------------------------------------------- */
  /* Oxide replaced an earlier #D2502A which FAILED WCAG twice over
     (3.78:1 as text, 4.27:1 for white-on-fill). This value passes both.   */
  --oxide:        #B93D19;   /*  4.97:1 on paper AA / white on it 5.61 AA   */
  --oxide-deep:   #A03211;   /* hover / active                              */
  --oxide-wash:   #F6E8E2;   /* faint tint for selected states              */

  /* Mint is a DARK-SURFACE-ONLY accent. Every mint candidate tested fails
     contrast on paper (they are light-on-light). Never put mint on paper.  */
  --mint:         #7FD4B8;   /*  7.14:1 on petrol AAA                       */
  --mint-2:       #A8E0CD;   /*  8.44:1 on petrol AAA                       */

  /* ---- LINES ---------------------------------------------------------- */
  --hairline:     #D8D2C4;   /* decorative rules (1.34:1 - intentional)     */
  --line:         #C9C2B1;   /* stronger decorative rule                    */
  --line-int:     #8F8776;   /* INTERACTIVE borders (inputs). 3.16:1 PASS   */
  --line-dark:    #1C4E50;   /* decorative rule on petrol                   */
  --line-dark-int:#548688;   /* interactive border on petrol. 3.05:1 PASS   */

  /* ---- STATES (all >= 4.5:1 on paper) --------------------------------- */
  --error:        #B3261E;   /* 5.79:1 AA */
  --success:      #0F6E4C;   /* 5.55:1 AA */
  --warning:      #8A5A00;   /* 5.25:1 AA */
  --error-wash:   #FBEAE8;
  --success-wash: #E6F2EC;

  /* ---- TYPE FAMILIES -------------------------------------------------- */
  --ff-display: "Archivo VF", "Archivo", ui-sans-serif, system-ui, sans-serif;
  --ff-body:    "Geist VF", "Geist", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --ff-mono:    "Geist Mono VF", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ---- FLUID TYPE SCALE ----------------------------------------------
     clamp() values are solved for a 360px -> 1920px viewport range, so
     there is no breakpoint where type jumps. Each comment shows min->max. */
  --fs-display: clamp(2.75rem, 2rem + 3.333vw, 6rem);            /* 44 -> 96 */
  --fs-h1:      clamp(2.25rem, 1.6rem + 2.9vw, 4.5rem);          /* 36 -> 72 */
  --fs-h2:      clamp(1.75rem, 1.35rem + 1.8vw, 3rem);           /* 28 -> 48 */
  --fs-h3:      clamp(1.3rem, 1.15rem + 0.7vw, 1.75rem);         /* 21 -> 28 */
  --fs-h4:      clamp(1.125rem, 1.05rem + 0.35vw, 1.375rem);     /* 18 -> 22 */
  --fs-lead:    clamp(1.0625rem, 1rem + 0.4vw, 1.3125rem);       /* 17 -> 21 */
  --fs-body:    clamp(1rem, 0.97rem + 0.14vw, 1.0625rem);        /* 16 -> 17 */
  --fs-sm:      0.9375rem;                                        /* 15 */
  --fs-xs:      0.8125rem;                                        /* 13 */
  --fs-mono:    0.75rem;                                          /* 12 */
  --fs-mono-sm: 0.6875rem;                                        /* 11 */

  --lh-tight:   1.02;
  --lh-snug:    1.14;
  --lh-normal:  1.5;
  --lh-relaxed: 1.62;

  /* Archivo width-axis presets. This is the signature typographic move. */
  --wd-headline: 74;   /* condensed - big display headlines                */
  --wd-title:    86;   /* semi-condensed - section titles                  */
  --wd-normal:  100;   /* normal - short labels, buttons                   */

  /* ---- SPACING (4px base) --------------------------------------------- */
  --s-1: 0.25rem;  --s-2: 0.5rem;   --s-3: 0.75rem;  --s-4: 1rem;
  --s-5: 1.5rem;   --s-6: 2rem;     --s-7: 3rem;     --s-8: 4rem;
  --s-9: 6rem;     --s-10: 8rem;

  /* Height of the fixed mobile action bar. body reserves this much bottom
     clearance on phones so the bar never covers the last line of the footer. */
  --action-bar-h: 3.5rem;
  --section-y: clamp(3.5rem, 1.5rem + 7vw, 8.5rem);

  /* ---- RADII (small on purpose - precision, not friendliness) --------- */
  --r-sm: 3px;  --r-md: 5px;  --r-lg: 8px;  --r-pill: 999px;

  /* ---- LAYOUT --------------------------------------------------------- */
  --w-max:    1280px;
  --w-narrow: 940px;
  --w-text:   68ch;
  --gutter:   clamp(1.25rem, 4vw, 3rem);
  --header-h: 68px;
  --header-h-condensed: 56px;

  /* ---- MOTION ---------------------------------------------------------
     Five durations, each with a job. Anything outside this set is a bug.  */
  --dur-1: 120ms;  /* micro feedback: press, checkbox, tiny state flip     */
  --dur-2: 200ms;  /* hover, focus ring, colour change                    */
  --dur-3: 320ms;  /* element reveal, tooltip, chip expand                 */
  --dur-4: 520ms;  /* accordion, panel open, section reveal               */
  --dur-5: 900ms;  /* page-load sequence only                             */

  /* Strong deceleration. The workhorse: things arrive and settle.        */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  /* Symmetric, for things that move and come back (accordion).           */
  --ease-io:  cubic-bezier(0.65, 0, 0.35, 1);
  /* Mechanical: zero overshoot. For anything that should feel machined.   */
  --ease-mech: cubic-bezier(0.3, 0, 0.2, 1);
  /* A real spring with only ~1.5% overshoot - present but not cartoonish. */
  --ease-spring: linear(
    0, 0.006, 0.025 2.8%, 0.101 6.1%, 0.539 18.9%, 0.721 25.3%,
    0.849 31.5%, 0.937 38.1%, 0.968 41.8%, 0.991 45.7%, 1.006 50.1%,
    1.015 60%, 1.006 76.2%, 1
  );

  --travel-sm: 8px;    /* small reveal rise   */
  --travel-md: 14px;   /* standard reveal     */
  --travel-lg: 28px;   /* hero / large panels */
  --stagger:   60ms;   /* between siblings    */

  /* ---- Z-INDEX -------------------------------------------------------- */
  --z-base: 0; --z-raised: 10; --z-sticky: 100; --z-header: 200;
  --z-overlay: 300; --z-modal: 400; --z-toast: 500;
}


/* ==========================================================================
   03  RESET & BASE
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Offset for the sticky header when jumping to an anchor. */
  scroll-padding-top: calc(var(--header-h) + var(--s-5));
  -moz-tab-size: 4; tab-size: 4;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--text);
  font-family: var(--ff-body);
  font-size: var(--fs-body);
  line-height: var(--lh-relaxed);
  font-weight: 400;
  /* Tabular numerals site-wide. Prices, phone numbers, counters and stats
     all contain digits; proportional figures make them jitter during
     count-up animations and misalign in the pricing table. */
  font-variant-numeric: tabular-nums lining-nums;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ul, ol { margin: 0; }
ul[class], ol[class] { list-style: none; padding: 0; }

img, picture, video, canvas, svg { display: block; max-width: 100%; }
img, video { height: auto; }

input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }

a { color: var(--oxide); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { color: var(--oxide-deep); }

/* Visible, high-contrast focus. Never remove this. */
:focus-visible {
  outline: 2px solid var(--oxide);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
.on-dark :focus-visible, .sect--dark :focus-visible { outline-color: var(--mint); }
:focus:not(:focus-visible) { outline: none; }

::selection { background: var(--petrol); color: var(--text-inv); }

/* Skip link — visible on focus only. */
.skip-link {
  position: absolute; top: 0; left: 50%;
  transform: translate(-50%, -110%);
  z-index: var(--z-toast);
  padding: var(--s-3) var(--s-5);
  background: var(--petrol); color: var(--text-inv);
  font-family: var(--ff-mono); font-size: var(--fs-mono);
  letter-spacing: 0.08em; text-transform: uppercase; text-decoration: none;
  border-radius: 0 0 var(--r-md) var(--r-md);
  transition: transform var(--dur-2) var(--ease-out);
}
.skip-link:focus { transform: translate(-50%, 0); color: var(--text-inv); }


/* ==========================================================================
   04  TYPOGRAPHY
   ========================================================================== */

.display, h1, h2, h3, h4 {
  font-family: var(--ff-display);
  line-height: var(--lh-snug);
  letter-spacing: -0.015em;
  text-wrap: balance;   /* progressive enhancement; must not be relied on */
  font-synthesis: none;
}

.display {
  font-size: var(--fs-display);
  font-weight: 700;
  font-stretch: calc(var(--wd-headline) * 1%);
  line-height: var(--lh-tight);
  letter-spacing: -0.028em;
}

h1 {
  font-size: var(--fs-h1);
  font-weight: 700;
  font-stretch: calc(var(--wd-headline) * 1%);
  line-height: var(--lh-tight);
  letter-spacing: -0.025em;
}

h2 {
  font-size: var(--fs-h2);
  font-weight: 650;
  font-stretch: calc(var(--wd-title) * 1%);
  letter-spacing: -0.02em;
}

h3 { font-size: var(--fs-h3); font-weight: 600; font-stretch: calc(var(--wd-title) * 1%); }
h4 { font-size: var(--fs-h4); font-weight: 600; font-stretch: calc(var(--wd-normal) * 1%); }

.lead {
  font-size: var(--fs-lead);
  line-height: 1.5;
  color: var(--text-2);
  text-wrap: pretty;
  max-width: 54ch;
}

p { text-wrap: pretty; }
.prose p + p { margin-top: var(--s-4); }
.prose { max-width: var(--w-text); }
.prose strong { font-weight: 600; color: var(--text); }

/* ---- The mono data label. Used for eyebrows, stats, spec rows. -------- */
.mono {
  font-family: var(--ff-mono);
  font-size: var(--fs-mono);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--text-3);
}
.mono--sm { font-size: var(--fs-mono-sm); letter-spacing: 0.11em; }
.sect--dark .mono, .on-dark .mono { color: var(--mint); }

/* Section marker: "§03 — TRANSPARENT PRICING" with a rule that draws in.
   The engineering-document reference that ties the whole site together. */
.marker {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  margin-bottom: var(--s-5);
}
.marker__num {
  font-family: var(--ff-mono);
  font-size: var(--fs-mono);
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--oxide);
  flex: none;
}
.sect--dark .marker__num { color: var(--mint); }
.marker__label {
  font-family: var(--ff-mono);
  font-size: var(--fs-mono);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-3);
  flex: none;
}
.sect--dark .marker__label { color: var(--text-inv-2); }
/* The rule draws left-to-right when the section enters the viewport. */
.marker__rule {
  flex: 1 1 auto;
  height: 1px;
  background: var(--line);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-4) var(--ease-out);
}
.sect--dark .marker__rule { background: var(--line-dark); }
.is-revealed .marker__rule, .marker.is-revealed .marker__rule { transform: scaleX(1); }


/* ==========================================================================
   05  LAYOUT PRIMITIVES
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--w-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--narrow { max-width: var(--w-narrow); }

.sect {
  padding-block: var(--section-y);
  position: relative;
}
/* content-visibility lets the browser skip layout for offscreen sections.
   contain-intrinsic-size prevents the scrollbar from jumping as they render. */
.sect--defer {
  content-visibility: auto;
  contain-intrinsic-size: auto 760px;
}
.sect--dark  { background: var(--petrol); color: var(--text-inv); }
.sect--dark h1, .sect--dark h2, .sect--dark h3, .sect--dark h4 { color: var(--text-inv); }
.sect--dark p, .sect--dark .lead { color: var(--text-inv-2); }
.sect--ink   { background: var(--ink); color: var(--text-inv); }
.sect--inset { background: var(--paper-2); }

/* Asymmetric two-column: heading rail + content. Not a centred layout. */
.rail {
  display: grid;
  gap: var(--s-6) var(--s-8);
  grid-template-columns: 1fr;
}
@media (min-width: 62rem) {
  .rail { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: start; }
  .rail--sticky > :first-child { position: sticky; top: calc(var(--header-h) + var(--s-6)); }
}

.grid { display: grid; gap: var(--s-5); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); }

.stack > * + * { margin-top: var(--flow, var(--s-4)); }
.cluster { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; }

/* Hairline graph-paper field. Technical-drawing texture, one gradient,
   no image request. Used behind the hero and dark panels. */
.grid-field { position: relative; isolation: isolate; }
.grid-field::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(to right, color-mix(in srgb, var(--line) 55%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in srgb, var(--line) 55%, transparent) 1px, transparent 1px);
  background-size: 56px 56px;
  /* Fade the grid out toward the edges so it reads as texture, not a table. */
  mask-image: radial-gradient(120% 90% at 50% 0%, #000 25%, transparent 78%);
}
.sect--dark .grid-field::before, .grid-field--dark::before {
  background-image:
    linear-gradient(to right, color-mix(in srgb, var(--line-dark) 90%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in srgb, var(--line-dark) 90%, transparent) 1px, transparent 1px);
}

/* Diagonal hatch — replaces gradients for emphasis blocks. */
.hatch {
  background-image: repeating-linear-gradient(
    45deg,
    color-mix(in srgb, var(--petrol) 12%, transparent) 0 1px,
    transparent 1px 7px
  );
}


/* ==========================================================================
   06  MOTION PRIMITIVES & SCROLL REVEALS
   --------------------------------------------------------------------------
   Two-tier strategy:

   TIER 1 (preferred) — native CSS scroll-driven animation via
   `animation-timeline: view()`. Runs off the main thread, costs zero JS, and
   cannot jank. Used for every simple "rise and fade in" reveal.

   TIER 2 — Motion's JS (`inView`, `animate`, `scroll`) only where we need
   sequencing, stagger keyed to real data, counters, or interaction state.

   The fallback chain matters: elements start at opacity 1 and are only
   hidden if the browser supports the animation, so a browser without
   scroll-driven animation and with JS disabled still shows all content.

   >>> OWNERSHIP RULE — the one invariant this file must never break <<<
   Anything that HIDES content must be revealed by the same layer that hid
   it: CSS scroll-timeline, or site.js. enhance.js (the Motion module) may
   only ever UPGRADE an animation that already works without it. It must
   never be the sole thing that makes content visible, because it is an ES
   module and a module can fail to load for reasons the page cannot detect
   — an old browser, a file:// origin, a blocked request. Content that
   depends on it would then be invisible with no error and no fallback.
   ========================================================================== */

/* Base state: visible. Nothing is hidden until we know we can reveal it. */
.reveal { --reveal-travel: var(--travel-md); }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal {
      animation: reveal-rise linear both;
      animation-timeline: view();
      /* Start when the element's top edge is 12% up from the viewport
         bottom; finish by the time it is 34% in. Feels responsive without
         the element being fully on screen before it settles. */
      animation-range: entry 6% cover 26%;
    }
    /* Stagger for siblings — set via --i on the element. */
    .reveal[style*="--i"] { animation-range: entry calc(6% + (var(--i) * 3%)) cover calc(26% + (var(--i) * 3%)); }
  }
}

@keyframes reveal-rise {
  from { opacity: 0; transform: translate3d(0, var(--reveal-travel), 0); }
  to   { opacity: 1; transform: none; }
}

/* Variants */
.reveal--sm { --reveal-travel: var(--travel-sm); }
.reveal--lg { --reveal-travel: var(--travel-lg); }
.reveal--fade { --reveal-travel: 0px; }

/* JS-driven reveal (Tier 2). The `.js` class is added by our script, so
   these rules never apply when JS is off. */
.js .reveal-js {
  opacity: 0;
  transform: translate3d(0, var(--travel-md), 0);
  will-change: opacity, transform;
}
.js .reveal-js.is-revealed {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--dur-4) var(--ease-out),
    transform var(--dur-4) var(--ease-out);
}
/* Drop will-change once the animation is done — leaving it on permanently
   pins a compositor layer per element and exhausts GPU memory on mobile. */
.js .reveal-js.is-settled { will-change: auto; }

/* Line that draws itself in. */
.draw-line {
  height: 1px; background: var(--line);
  transform: scaleX(0); transform-origin: left center;
}
.is-revealed .draw-line { transform: scaleX(1); transition: transform var(--dur-4) var(--ease-out); }


/* ==========================================================================
   07  COMPONENTS
   ========================================================================== */

/* ---- 7.1  BUTTONS -----------------------------------------------------
   The hover is a fill that WIPES across from the left, not a colour fade.
   Mechanical, matches the brand, and animates only transform. */

.btn {
  --btn-bg: var(--oxide);
  --btn-fg: var(--text-on-cta);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: 0.82em 1.5em;
  border-radius: var(--r-md);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--ff-display);
  font-size: var(--fs-sm);
  font-weight: 650;
  font-stretch: calc(var(--wd-normal) * 1%);
  letter-spacing: 0.005em;
  text-decoration: none;
  white-space: nowrap;
  isolation: isolate;
  overflow: hidden;
  cursor: pointer;
  transition:
    transform var(--dur-1) var(--ease-mech),
    box-shadow var(--dur-2) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.btn::after {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  background: var(--oxide-deep);
  transform: scaleX(0); transform-origin: left center;
  transition: transform var(--dur-3) var(--ease-out);
}
.btn:hover::after { transform: scaleX(1); }
.btn:hover { color: var(--btn-fg); }
.btn:active { transform: translateY(1px); }

.btn__icon { flex: none; width: 1em; height: 1em; }

/* Secondary: outlined, on paper. */
.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--text);
  box-shadow: inset 0 0 0 1px var(--line-int);
}
.btn--ghost::after { background: var(--petrol); }
.btn--ghost:hover { --btn-fg: var(--text-inv); box-shadow: inset 0 0 0 1px var(--petrol); }

/* On dark sections. */
.btn--onDark { --btn-bg: var(--mint); --btn-fg: var(--petrol-3); }
.btn--onDark::after { background: var(--mint-2); }

.btn--lg { padding: 1em 1.9em; font-size: var(--fs-body); }
.btn--block { width: 100%; }

/* ---- 7.2  HEADER ------------------------------------------------------
   Condenses on scroll. Only height/background/blur change, and the change
   is driven by a single class toggle from a throttled scroll listener, not
   per-frame work. */

.header {
  position: sticky; top: 0; z-index: var(--z-header);
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid transparent;
  transition:
    border-color var(--dur-2) var(--ease-out),
    background-color var(--dur-2) var(--ease-out);
}
.header.is-stuck { border-bottom-color: var(--hairline); }

.header__inner {
  display: flex; align-items: center; gap: var(--s-5);
  height: var(--header-h);
  transition: height var(--dur-3) var(--ease-out);
}
.header.is-stuck .header__inner { height: var(--header-h-condensed); }

.logo {
  display: inline-flex; align-items: center; gap: var(--s-3);
  text-decoration: none; color: var(--text); flex: none;
}
.logo__mark { width: 30px; height: 30px; flex: none; }
.logo__text {
  font-family: var(--ff-display);
  font-size: 1.0625rem; font-weight: 700;
  font-stretch: calc(var(--wd-title) * 1%);
  letter-spacing: -0.015em;
}
.logo__text span { color: var(--oxide); }

.nav { display: none; margin-inline-start: auto; }
@media (min-width: 64rem) { .nav { display: flex; align-items: center; gap: var(--s-5); } }

.nav__link {
  position: relative;
  font-size: var(--fs-sm); font-weight: 500;
  color: var(--text-2); text-decoration: none;
  padding-block: var(--s-2);
  transition: color var(--dur-2) var(--ease-out);
}
.nav__link::after {
  content: ""; position: absolute;
  left: 0; right: 0; bottom: 0; height: 1.5px;
  background: var(--oxide);
  transform: scaleX(0); transform-origin: left center;
  transition: transform var(--dur-2) var(--ease-out);
}
.nav__link:hover { color: var(--text); }
.nav__link:hover::after, .nav__link[aria-current="page"]::after { transform: scaleX(1); }
.nav__link[aria-current="page"] { color: var(--text); }

.header__actions { display: flex; align-items: center; gap: var(--s-3); margin-inline-start: auto; }
@media (min-width: 64rem) { .header__actions { margin-inline-start: 0; } }

/* Phone link in the header — mono, because it is data. */
.header__tel {
  display: none; align-items: center; gap: var(--s-2);
  font-family: var(--ff-mono); font-size: var(--fs-sm);
  color: var(--text); text-decoration: none;
  font-variant-numeric: tabular-nums;
}
@media (min-width: 48rem) { .header__tel { display: inline-flex; } }
.header__tel:hover { color: var(--oxide); }

/* Mobile menu button */
.burger {
  display: inline-flex; flex-direction: column; justify-content: center; gap: 5px;
  width: 42px; height: 42px; padding: 0 9px;
  border-radius: var(--r-md);
  box-shadow: inset 0 0 0 1px var(--line-int);
}
@media (min-width: 64rem) { .burger { display: none; } }
.burger span {
  display: block; height: 1.5px; background: var(--text);
  transition: transform var(--dur-3) var(--ease-out), opacity var(--dur-1) linear;
}
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

/* Mobile drawer uses grid-template-rows so it animates height without
   animating `height` (which would trigger layout on every frame). */
.mobile-nav {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-4) var(--ease-io);
  background: var(--paper);
  border-bottom: 1px solid transparent;
}
.mobile-nav.is-open { grid-template-rows: 1fr; border-bottom-color: var(--hairline); }
.mobile-nav > div { overflow: hidden; }
@media (min-width: 64rem) { .mobile-nav { display: none; } }
.mobile-nav a {
  display: block; padding: var(--s-4) var(--gutter);
  border-top: 1px solid var(--hairline);
  color: var(--text); text-decoration: none;
  font-family: var(--ff-display); font-weight: 600;
  font-stretch: calc(var(--wd-title) * 1%);
}

/* ---- 7.3  PANELS / CARDS ---------------------------------------------
   No drop shadows. Elevation is a hairline plus, on hover, an offset solid
   edge that reads like a print registration mark. */

.panel {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: var(--s-5);
  transition:
    transform var(--dur-3) var(--ease-out),
    border-color var(--dur-2) var(--ease-out);
}
.panel--flat { background: transparent; border-color: var(--line); }
.sect--dark .panel {
  background: var(--petrol-2); border-color: var(--line-dark); color: var(--text-inv);
}

/* Interactive panel: lifts 3px and drops an offset petrol edge behind it. */
.panel--link { cursor: pointer; }
.panel--link::after {
  content: ""; position: absolute; inset: 0;
  border-radius: inherit;
  border: 1px solid var(--petrol);
  transform: translate(0, 0);
  opacity: 0;
  transition: transform var(--dur-3) var(--ease-out), opacity var(--dur-2) linear;
  pointer-events: none;
}
.panel--link:hover, .panel--link:focus-within { transform: translateY(-3px); border-color: var(--petrol); }
.panel--link:hover::after, .panel--link:focus-within::after { transform: translate(5px, 5px); opacity: 1; }
.sect--dark .panel--link::after { border-color: var(--mint); }

/* Corner crop-marks — used sparingly on hero/quote panels. */
.crop { position: relative; }
.crop::before, .crop::after {
  content: ""; position: absolute; width: 10px; height: 10px;
  border-color: var(--oxide); border-style: solid; border-width: 0;
}
.crop::before { top: -1px; left: -1px; border-top-width: 2px; border-left-width: 2px; }
.crop::after { bottom: -1px; right: -1px; border-bottom-width: 2px; border-right-width: 2px; }

/* ---- 7.4  SPEC ROW (leader-dot data row) ------------------------------ */
.spec {
  display: flex; align-items: baseline; gap: var(--s-3);
  font-size: var(--fs-sm);
  padding-block: var(--s-2);
}
.spec__k { color: var(--text-3); flex: none; }
.spec__dots {
  flex: 1 1 auto; height: 1px; min-width: var(--s-5);
  border-bottom: 1px dotted var(--line);
  transform: translateY(-3px);
}
.spec__v { font-family: var(--ff-mono); color: var(--text); flex: none; font-variant-numeric: tabular-nums; }
.sect--dark .spec__k { color: var(--text-inv-2); }
.sect--dark .spec__v { color: var(--mint); }
.sect--dark .spec__dots { border-bottom-color: var(--line-dark); }

/* ---- 7.5  STAT / COUNTER --------------------------------------------- */
.stat__value {
  font-family: var(--ff-display);
  font-size: var(--fs-h2);
  font-weight: 700;
  font-stretch: calc(var(--wd-headline) * 1%);
  line-height: 1;
  letter-spacing: -0.02em;
  /* Reserve digit width so the count-up cannot reflow the layout. */
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.sect--dark .stat__value { color: var(--mint); }
.stat__label { margin-top: var(--s-2); }

/* ---- 7.6  TRUST CHIP ------------------------------------------------- */
.chip {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: 0.35em 0.7em 0.35em 0.5em;
  border-radius: var(--r-sm);
  background: var(--paper-2);
  box-shadow: inset 0 0 0 1px var(--hairline);
  font-size: var(--fs-xs); font-weight: 500;
  color: var(--text-2);
  white-space: nowrap;
}
.chip svg { width: 14px; height: 14px; flex: none; color: var(--success); }
.sect--dark .chip {
  background: color-mix(in srgb, var(--mint) 12%, transparent);
  box-shadow: inset 0 0 0 1px var(--line-dark);
  color: var(--text-inv);
}
.sect--dark .chip svg { color: var(--mint); }

/* ---- 7.7  STICKY MOBILE ACTION BAR ----------------------------------
   The single most important conversion element on mobile. Always reachable.
   Body gets bottom padding so it never covers the footer's last line. */
.action-bar {
  /* Declared as a variable so §10.2 can reserve matching body clearance. */
  --action-bar-h: 3.5rem;
  position: fixed; left: 0; right: 0; bottom: 0;
  z-index: var(--z-sticky);
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--line-dark);
  padding-bottom: env(safe-area-inset-bottom, 0);
  transform: translateY(101%);
  transition: transform var(--dur-4) var(--ease-out);
}
.action-bar.is-visible { transform: none; }
@media (min-width: 48rem) { .action-bar { display: none; } }
.action-bar a {
  display: flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: var(--s-4) var(--s-3);
  background: var(--petrol); color: var(--text-inv);
  font-family: var(--ff-display); font-weight: 650;
  font-stretch: calc(var(--wd-normal) * 1%);
  font-size: var(--fs-sm); text-decoration: none;
  min-height: 52px;   /* comfortable thumb target */
}
.action-bar a.is-primary { background: var(--oxide); color: var(--text-on-cta); }
.action-bar svg { width: 17px; height: 17px; flex: none; }

/* ---- 7.8  FORMS ----------------------------------------------------- */
.field { display: block; }
.field + .field { margin-top: var(--s-4); }
.field__label {
  display: block; margin-bottom: var(--s-2);
  font-family: var(--ff-mono); font-size: var(--fs-mono);
  letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-3);
}
.field__label .req { color: var(--oxide); }

.input, .select, .textarea {
  width: 100%;
  padding: 0.8em 0.9em;
  background: var(--surface);
  border: 1px solid var(--line-int);   /* 3.16:1 — interactive boundary */
  border-radius: var(--r-md);
  font-size: var(--fs-body);
  color: var(--text);
  transition: border-color var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
  appearance: none;
}
.input::placeholder, .textarea::placeholder { color: var(--text-3); opacity: 1; }
.input:hover, .select:hover, .textarea:hover { border-color: var(--text-3); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--petrol);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--petrol) 18%, transparent);
}
.select {
  /* Chevron drawn as an inline SVG data URI — no icon request. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%235A6266' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.9em center;
  padding-right: 2.4em;
}
.textarea { min-height: 7rem; resize: vertical; }

/* Validation. `aria-invalid` drives the style so the visual and the
   accessible state can never disagree. */
.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] {
  border-color: var(--error);
  background: var(--error-wash);
}
.field__error {
  display: none;
  margin-top: var(--s-2);
  font-size: var(--fs-xs);
  color: var(--error);
}
.field__error.is-shown { display: flex; gap: var(--s-2); align-items: flex-start; }
.js .field__error.is-shown { animation: field-shake var(--dur-3) var(--ease-out); }
@keyframes field-shake {
  0%, 100% { transform: translateX(0); }
  22% { transform: translateX(-3px); }
  58% { transform: translateX(2px); }
}

/* Honeypot — must be reachable by nothing, including screen readers. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---- 7.9  ACCORDION (FAQ + troubleshooting) -------------------------
   grid-template-rows 0fr -> 1fr animates the panel without animating
   height, so no layout thrash. Built on <details> so it works with JS off. */
.acc { border-top: 1px solid var(--hairline); }
.acc__item { border-bottom: 1px solid var(--hairline); }
.acc__item > summary {
  display: flex; align-items: flex-start; gap: var(--s-4);
  padding: var(--s-5) 0;
  cursor: pointer;
  list-style: none;
  font-family: var(--ff-display);
  font-size: var(--fs-h4); font-weight: 600;
  font-stretch: calc(var(--wd-title) * 1%);
  color: var(--text);
  transition: color var(--dur-2) var(--ease-out);
}
.acc__item > summary::-webkit-details-marker { display: none; }
.acc__item > summary:hover { color: var(--oxide); }
.acc__q { flex: 1 1 auto; }
.acc__sign {
  flex: none; width: 20px; height: 20px; margin-top: 0.15em;
  position: relative;
}
.acc__sign::before, .acc__sign::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  background: var(--oxide);
}
.acc__sign::before { width: 100%; height: 1.5px; }
.acc__sign::after {
  width: 1.5px; height: 100%;
  transition: transform var(--dur-3) var(--ease-out);
}
.acc__item[open] .acc__sign::after { transform: scaleY(0); }
.acc__panel { overflow: hidden; }
.acc__panel > div { padding-bottom: var(--s-5); max-width: var(--w-text); color: var(--text-2); }

/* ---- 7.10  MARQUEE (brands) -----------------------------------------
   Pure CSS translate on a duplicated track. Pauses on hover AND focus-within
   (keyboard users must be able to stop it), and is replaced by a static grid
   under reduced motion. */
.marquee {
  --marquee-dur: 42s;
  position: relative;
  overflow: hidden;
  mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}
.marquee__track {
  display: flex; width: max-content;
  animation: marquee-scroll var(--marquee-dur) linear infinite;
}
.marquee:hover .marquee__track,
.marquee:focus-within .marquee__track { animation-play-state: paused; }
@keyframes marquee-scroll { to { transform: translate3d(-50%, 0, 0); } }
.marquee__item {
  display: flex; align-items: center; justify-content: center;
  padding-inline: var(--s-6);
  font-family: var(--ff-display);
  font-size: var(--fs-h4); font-weight: 600;
  font-stretch: calc(var(--wd-title) * 1%);
  color: var(--text-inv-2);
  white-space: nowrap;
}

/* ---- 7.11  TABLE (pricing) ----------------------------------------- */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: var(--surface);
  -webkit-overflow-scrolling: touch;
}
.table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.table th, .table td { padding: var(--s-4); text-align: left; }
.table thead th {
  font-family: var(--ff-mono); font-size: var(--fs-mono);
  letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-3); font-weight: 500;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  position: sticky; top: 0; background: var(--surface); z-index: 1;
}
.table tbody tr + tr { border-top: 1px solid var(--hairline); }
.table tbody tr { transition: background-color var(--dur-2) linear; }
.table tbody tr:hover { background: var(--paper-2); }
.table td.num { font-family: var(--ff-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.table td.svc { font-weight: 500; color: var(--text); }

/* ---- 7.12  VIDEO --------------------------------------------------- */
.video {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--petrol-3);
  border: 1px solid var(--line-dark);
}
.video video { width: 100%; height: 100%; object-fit: cover; display: block; }
.video__cap {
  position: absolute; left: var(--s-4); bottom: var(--s-4);
  display: flex; align-items: center; gap: var(--s-2);
  padding: 0.4em 0.7em;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--petrol-3) 72%, transparent);
  backdrop-filter: blur(8px);
  font-family: var(--ff-mono); font-size: var(--fs-mono-sm);
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--mint);
}


/* ==========================================================================
   08  UTILITIES
   ========================================================================== */

.vh {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}
.text-center { text-align: center; }
.mt-0 { margin-top: 0; }
.mt-4 { margin-top: var(--s-4); }
.mt-5 { margin-top: var(--s-5); }
.mt-6 { margin-top: var(--s-6); }
.mt-7 { margin-top: var(--s-7); }
.flow-tight { --flow: var(--s-3); }
.flow-loose { --flow: var(--s-5); }
.nowrap { white-space: nowrap; }
.tnum { font-variant-numeric: tabular-nums; }
.oxide { color: var(--oxide); }
.mint { color: var(--mint); }


/* ==========================================================================
   09  REDUCED MOTION / FORCED COLOURS / PRINT
   --------------------------------------------------------------------------
   The reduced-motion policy is deliberately NOT "turn everything off".
   Blanket removal loses meaning: an accordion that snaps open with no
   transition is genuinely harder to follow. So:
     - Travel and scale animation: removed entirely.
     - Opacity cross-fades: kept, but shortened.
     - Looping/ambient motion (marquee, video): stopped completely.
     - Anything conveying state change (accordion, drawer): kept as a fast
       cross-fade so the change is still perceivable.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* Re-enable short, non-moving cross-fades where they carry meaning. */
  .acc__panel, .mobile-nav, .field__error, .reveal-js {
    transition-duration: 140ms !important;
    transition-property: opacity !important;
  }
  .reveal, .reveal-js { opacity: 1 !important; transform: none !important; animation: none !important; }
  .marquee__track { animation: none !important; transform: none !important; }
  .marquee { mask-image: none; overflow-x: auto; }
  .marquee__track { flex-wrap: wrap; width: 100%; justify-content: center; }
  .draw-line, .marker__rule { transform: scaleX(1) !important; }
  .btn::after { display: none; }
}

@media (prefers-reduced-transparency: reduce) {
  .header { background: var(--paper); backdrop-filter: none; -webkit-backdrop-filter: none; }
  .video__cap { background: var(--petrol-3); backdrop-filter: none; }
}

@media (forced-colors: active) {
  .panel, .table-wrap, .input, .select, .textarea { border: 1px solid CanvasText; }
  .btn { border: 1px solid ButtonText; }
  .marker__rule, .draw-line { background: CanvasText; transform: scaleX(1); }
  .grid-field::before, .hatch { display: none; }
}

@media print {
  .header, .action-bar, .mobile-nav, .marquee, video, .skip-link { display: none !important; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .sect { padding-block: 1rem; page-break-inside: avoid; }
  .sect--dark { background: #fff !important; color: #000 !important; }
  .sect--dark * { color: #000 !important; }
  a { color: #000; text-decoration: underline; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; }
  .acc__panel { display: block !important; }
  details { display: block; }
  details > summary { list-style: none; }
}


/* ==========================================================================
   07b  PAGE COMPONENTS
   ========================================================================== */

/* ---- 7.13  HERO -------------------------------------------------------
   Asymmetric: copy on the left at 6/11, illustration at 5/11. Deliberately
   NOT a centred hero over a background photo, which is the category default.
   ----------------------------------------------------------------------- */
.hero {
  position: relative;
  padding-block: clamp(2.5rem, 1rem + 5vw, 5.5rem) clamp(3rem, 1rem + 6vw, 7rem);
  overflow: hidden;
}
.hero__layer {
  position: absolute; inset: -10% 0 0 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, color-mix(in srgb, var(--line) 60%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in srgb, var(--line) 60%, transparent) 1px, transparent 1px);
  background-size: 60px 60px;
  mask-image: radial-gradient(115% 85% at 62% 12%, #000 20%, transparent 74%);
  will-change: transform;
}
.hero__inner {
  position: relative;
  display: grid;
  gap: var(--s-7);
  align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 60rem) {
  .hero__inner { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); gap: var(--s-8); }
}
.hero__eyebrow { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-5); }

/* Live availability dot. The only looping animation on the page — it earns
   its keep by signalling "someone is actually there right now". */
.pulse {
  position: relative; flex: none;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--success);
}
.pulse::after {
  content: ""; position: absolute; inset: 0;
  border-radius: 50%; background: var(--success);
  animation: pulse-ring 2.4s var(--ease-out) infinite;
}
@keyframes pulse-ring {
  0%   { transform: scale(1); opacity: 0.55; }
  70%  { transform: scale(3.2); opacity: 0; }
  100% { transform: scale(3.2); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .pulse::after { animation: none; } }

.hero .lead { margin-top: var(--s-5); }
.hero__cta { margin-top: var(--s-6); gap: var(--s-4); }
.hero__chips { margin-top: var(--s-6); gap: var(--s-2) var(--s-3); }

.hero__figure { position: relative; }
.machine { width: 100%; max-width: 25rem; margin-inline: auto; height: auto; }
/* var() inside SVG presentation attributes is well supported, but the text
   element needs the font applied through CSS to pick up the custom family. */
.machine text {
  font-family: var(--ff-mono);
  font-size: 13px;
  letter-spacing: 0.08em;
}

/* ---- 7.14  TRUST BAND ------------------------------------------------- */
.band { padding-block: clamp(2.5rem, 1rem + 4vw, 4.5rem); }

/* The sweep rule. Owned by CSS + site.js (the `.is-revealed` class), NOT by
   enhance.js — see the note in section 06. It defaults to scaleX(1) so that
   if no JS runs at all it is simply drawn, never missing. */
.band__rule {
  height: 1px; background: var(--mint);
  margin-bottom: var(--s-6);
  opacity: 0.5;
  transform-origin: left center;
}
.js .band__rule { transform: scaleX(0); }
.js .band[data-reveal].is-revealed .band__rule,
.js .band.is-revealed .band__rule {
  transform: scaleX(1);
  transition: transform 900ms var(--ease-out);
}

.band__grid { gap: var(--s-6) var(--s-5); }
.band__grid > li { min-width: 0; }

/* ---- 7.15  SYMPTOM PICKER -------------------------------------------- */
.sym__grid { gap: var(--s-3); }

.sym {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: auto auto;
  gap: 0 var(--s-3);
  align-items: center;
  text-align: left;
  padding: var(--s-4);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  transition:
    border-color var(--dur-2) var(--ease-out),
    background-color var(--dur-2) var(--ease-out);
}
.sym svg {
  grid-row: 1 / span 2;
  width: 26px; height: 26px;
  color: var(--text-3);
  transition: color var(--dur-2) var(--ease-out);
}
.sym__label {
  font-family: var(--ff-display);
  font-size: var(--fs-body); font-weight: 600;
  font-stretch: calc(var(--wd-normal) * 1%);
  color: var(--text);
}
.sym__hint { display: block; margin-top: 2px; text-transform: none; letter-spacing: 0.04em; }

.sym:hover { border-color: var(--line-int); }
.sym:hover svg { color: var(--oxide); }

/* Selected state. The left edge becomes a solid oxide bar — a discrete,
   unmistakable "this one" marker rather than a colour wash. */
.sym[aria-pressed="true"] {
  border-color: var(--oxide);
  background: var(--oxide-wash);
}
.sym[aria-pressed="true"]::before {
  content: ""; position: absolute;
  left: -1px; top: -1px; bottom: -1px; width: 3px;
  background: var(--oxide);
  border-radius: var(--r-md) 0 0 var(--r-md);
}
.sym[aria-pressed="true"] svg { color: var(--oxide); }

/* ---- 7.16  DIAGNOSIS OUTPUT ------------------------------------------ */
.sym-out {
  margin-top: var(--s-5);
  padding: var(--s-6);
  background: var(--petrol);
  color: var(--text-inv);
  border-radius: var(--r-lg);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--dur-4) var(--ease-out), transform var(--dur-4) var(--ease-out);
}
.sym-out.is-revealed { opacity: 1; transform: none; }
.no-js .sym-out { opacity: 1; transform: none; }
.sym-out__eyebrow { color: var(--mint); margin-bottom: var(--s-2); }
.sym-out h3 { color: var(--text-inv); }

.sym-out__list { margin-top: var(--s-5); display: grid; gap: var(--s-4); }
.sym-out__list dt { color: var(--mint); margin-bottom: var(--s-1); }
.sym-out__list dd { margin: 0; color: var(--text-inv-2); font-size: var(--fs-sm); }

.sym-out__figures {
  margin-top: var(--s-5);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line-dark);
}
.sym-out__note {
  text-transform: none; letter-spacing: 0.02em;
  line-height: 1.6; color: var(--text-inv-2);
}
.sym-out .crop::before, .sym-out .crop::after { border-color: var(--mint); }
.sym-out.crop::before, .sym-out.crop::after { border-color: var(--mint); }


/* ==========================================================================
   07c  PAGE COMPONENTS — SERVICES, PRICING, TIMELINE, PROOF, FOOTER
   --------------------------------------------------------------------------
   Continues §07b. Split only so the file stays navigable; there is no
   behavioural difference between the two blocks.
   ========================================================================== */

/* --------------------------------------------------------------------------
   SERVICE CARDS  (§02)
   The icon sits in a squared well with a hairline, not a coloured circle.
   Circles-with-icons is the single most template-coded pattern there is; a
   square well with a register mark reads like a parts catalogue instead.
   -------------------------------------------------------------------------- */
.svc__icon {
  width: 3rem;
  height: 3rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--r-2);
  background: var(--paper-2);
  color: var(--petrol);
  margin-bottom: var(--s-5);
  transition: background var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out),
              color var(--dur-2) var(--ease-out);
}
.svc__icon svg { width: 1.5rem; height: 1.5rem; }

/* On hover the well inverts to petrol. One property changing colour is
   enough; moving the icon as well would be noise. */
.panel--link:hover .svc__icon,
.panel--link:focus-visible .svc__icon {
  background: var(--petrol);
  border-color: var(--petrol);
  color: var(--mint);
}

.svc__price {
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid var(--hairline);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
}
.svc__from {
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-3);
}
.svc__amount {
  font-family: var(--ff-display);
  font-stretch: calc(var(--wd-title) * 1%);
  font-weight: 700;
  font-size: var(--fs-h4);
  color: var(--oxide);
  font-variant-numeric: tabular-nums lining-nums;
}


/* --------------------------------------------------------------------------
   PRICING  (§03)
   -------------------------------------------------------------------------- */
.pricing__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s-5);
  margin-bottom: var(--s-7);
}
.pricing__head > :first-child { flex: 1 1 24rem; min-width: 0; }

/* Only shown on narrow screens, where the table actually scrolls. */
.table-hint {
  display: none;
  margin-top: var(--s-3);
  font-size: var(--fs-xs);
  color: var(--text-3);
  align-items: center;
  gap: var(--s-2);
}
.table-hint svg { width: 1rem; height: 1rem; flex: none; color: var(--oxide); }

.pricing__notes {
  margin-top: var(--s-6);
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  padding-top: var(--s-5);
  border-top: 1px solid var(--hairline);
}
.pricing__notes li {
  position: relative;
  padding-left: var(--s-5);
  font-size: var(--fs-sm);
  color: var(--text-2);
  line-height: 1.55;
}
/* A mono dash instead of a bullet — matches the spec-sheet language. */
.pricing__notes li::before {
  content: "—";
  position: absolute;
  left: 0;
  top: 0;
  font-family: var(--ff-mono);
  color: var(--oxide);
}


/* --------------------------------------------------------------------------
   PROCESS TIMELINE  (§04)
   --------------------------------------------------------------------------
   The vertical rule is drawn by scroll position. Two implementations:

     1. CSS scroll-timeline where supported (compositor thread, no JS).
     2. enhance.js scaleY via Motion, where it is not.

   The rule is DECORATIVE ONLY — the numbered markers carry the sequence, so
   if neither implementation runs, nothing is lost but the flourish. Its
   default state is scaleY(1), i.e. fully drawn. Per the OWNERSHIP RULE in
   §06, no content is ever hidden by something that might not run.
   -------------------------------------------------------------------------- */
.timeline {
  position: relative;
  margin-top: var(--s-8);
  display: grid;
  gap: var(--s-7);
  counter-reset: step;
}

.timeline__rule {
  position: absolute;
  left: calc(var(--tl-marker) / 2 - 0.5px);
  top: var(--tl-marker);
  bottom: var(--tl-marker);
  width: 1px;
  background: var(--line);
  transform-origin: top center;
  pointer-events: none;
}
/* An oxide segment rides the top of the drawn portion, so you can see
   where the line has got to rather than just that it is long. */
.timeline__rule::after {
  content: "";
  position: absolute;
  left: -1px;
  bottom: 0;
  width: 3px;
  height: 2.5rem;
  background: linear-gradient(to top, var(--oxide), transparent);
}

.timeline { --tl-marker: 2.75rem; }

@supports (animation-timeline: view()) {
  .timeline__rule {
    transform: scaleY(0);
    animation: draw-y linear both;
    animation-timeline: view();
    /* `cover`, not `entry`/`exit`. The timeline block is ~1700px tall — far
       taller than any viewport — and for elements taller than the scrollport
       the entry and exit ranges each span (elementHeight + viewportHeight),
       so `entry 68%` lands somewhere the visitor never reaches and the rule
       stays at scaleY(0) forever. `cover` spans the whole intersection, which
       is what "draw as I scroll through this" actually means. */
    animation-range: cover 10% cover 78%;
  }
}
@keyframes draw-y { from { transform: scaleY(0); } to { transform: scaleY(1); } }

.timeline__step {
  position: relative;
  display: grid;
  grid-template-columns: var(--tl-marker) 1fr;
  gap: var(--s-5);
  align-items: start;
}

.timeline__marker {
  width: var(--tl-marker);
  height: var(--tl-marker);
  display: grid;
  place-items: center;
  border: 1px solid var(--line-int);
  border-radius: 50%;
  background: var(--paper);
  font-family: var(--ff-mono);
  font-size: var(--fs-sm);
  font-weight: 500;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--petrol);
  position: relative;
  z-index: 1;
  flex: none;
}
.timeline__step:last-child .timeline__marker {
  border-color: var(--oxide);
  background: var(--oxide);
  color: var(--text-on-cta);
}

.timeline__body { min-width: 0; padding-bottom: var(--s-2); }
.timeline__body h3 { font-size: var(--fs-h4); }
.timeline__body p { margin-top: var(--s-3); color: var(--text-2); max-width: 46ch; }

.timeline__meta {
  margin-top: var(--s-4);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
}

/* Dark-section variant, used on the inner pages. */
.sect--dark .timeline__rule { background: var(--line-dark); }
.sect--dark .timeline__marker {
  background: var(--petrol);
  border-color: var(--line-dark-int);
  color: var(--mint);
}
.sect--dark .timeline__body p { color: var(--text-inv-2); }

@media (max-width: 40rem) {
  .timeline { --tl-marker: 2.25rem; gap: var(--s-6); }
  .timeline__step { gap: var(--s-4); }
}


/* --------------------------------------------------------------------------
   WHY US  (§05)
   -------------------------------------------------------------------------- */
.why__specs {
  margin-top: var(--s-6);
  padding-top: var(--s-5);
  border-top: 1px solid var(--line-dark);
}
.why__specs .spec { margin-top: var(--s-3); }
.why__specs .spec:first-child { margin-top: 0; }


/* --------------------------------------------------------------------------
   BRANDS MARQUEE
   -------------------------------------------------------------------------- */
.brands {
  padding-block: var(--s-8);
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  background: var(--paper-2);
  overflow: hidden;
}
.brands__title {
  text-align: center;
  color: var(--text-3);
  margin-bottom: var(--s-6);
}
.brands .marquee__item {
  font-family: var(--ff-display);
  font-stretch: calc(var(--wd-title) * 1%);
  font-weight: 600;
  font-size: var(--fs-lg);
  color: var(--petrol);
  padding-inline: var(--s-5);
  white-space: nowrap;
  opacity: 0.72;
}
/* A hairline separator between brand names, drawn with a pseudo-element so
   the duplicated marquee track needs no extra markup. */
.brands .marquee__item + .marquee__item { position: relative; }
.brands .marquee__item + .marquee__item::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 1px;
  height: 1em;
  translate: 0 -50%;
  background: var(--line);
}


/* --------------------------------------------------------------------------
   TESTIMONIALS  (§06)
   -------------------------------------------------------------------------- */
.quote {
  display: flex;
  flex-direction: column;
  margin: 0;
}
.quote blockquote { margin: 0; flex: 1 1 auto; }
.quote blockquote p {
  font-size: var(--fs-md);
  line-height: 1.6;
  color: var(--text);
  /* An oversized opening quote as a register mark rather than decoration. */
  text-indent: 0;
}
.quote blockquote p::before {
  content: "\201C";
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: 1.6em;
  line-height: 0;
  color: var(--oxide);
  margin-right: 0.08em;
  vertical-align: -0.16em;
}
.quote__stars {
  color: var(--oxide);
  font-size: var(--fs-sm);
  letter-spacing: 0.14em;
  margin-bottom: var(--s-4);
}
.quote__by {
  margin-top: var(--s-5);
  padding-top: var(--s-4);
  border-top: 1px solid var(--hairline);
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
.quote__name {
  font-family: var(--ff-display);
  font-stretch: calc(var(--wd-title) * 1%);
  font-weight: 600;
  color: var(--text);
}
.quote__by .mono { color: var(--text-3); }


/* --------------------------------------------------------------------------
   SERVICE AREAS LIST  (§08)
   Two columns of hairline-separated links. No pills, no icons — a directory.
   -------------------------------------------------------------------------- */
.areas__list {
  columns: 2;
  column-gap: var(--s-6);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 30rem) { .areas__list { columns: 2; } }
@media (max-width: 24rem) { .areas__list { columns: 1; } }

.areas__list li { break-inside: avoid; }
.areas__list a {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  padding-block: var(--s-3);
  border-bottom: 1px solid var(--hairline);
  color: var(--text);
  text-decoration: none;
  font-size: var(--fs-sm);
  transition: color var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out),
              padding-left var(--dur-2) var(--ease-out);
}
/* A mono arrow that only appears on hover, and nudges the label. Cheap,
   but it makes 24 static links feel like 24 buttons. */
.areas__list a::after {
  content: "→";
  font-family: var(--ff-mono);
  margin-left: auto;
  opacity: 0;
  translate: -4px 0;
  transition: opacity var(--dur-2) var(--ease-out), translate var(--dur-2) var(--ease-out);
}
.areas__list a:hover,
.areas__list a:focus-visible {
  color: var(--oxide);
  border-color: var(--oxide);
  padding-left: var(--s-2);
}
.areas__list a:hover::after,
.areas__list a:focus-visible::after { opacity: 1; translate: 0 0; }


/* --------------------------------------------------------------------------
   BOOKING  (§ book)
   -------------------------------------------------------------------------- */
.book__contact .spec { margin-top: var(--s-3); }
.book__contact .spec__k { color: var(--text-inv-2); }
.book__contact .spec__v,
.book__contact .spec__v a { color: var(--text-inv); }
.book__contact .spec__v a:hover { color: var(--mint); }
.book__contact .spec__dots { border-bottom-color: var(--line-dark); }

/* The form panel is a light card on the dark section — the only place on the
   site where paper sits on petrol. It makes the form unmistakably the thing
   to do here without needing an arrow pointing at it. */
.book__form {
  background: var(--surface);
  border-color: var(--line);
}
.book__form .field + .field { margin-top: var(--s-5); }
.book__note {
  text-transform: none;
  letter-spacing: 0.01em;
  line-height: 1.6;
  color: var(--text-3);
}
.book__note a { color: var(--text-2); }

@media (max-width: 60rem) {
  .book .rail { gap: var(--s-8); }
}


/* --------------------------------------------------------------------------
   FOOTER
   -------------------------------------------------------------------------- */
.footer {
  background: var(--ink);
  color: var(--text-inv-2);
  padding-block: var(--s-9) var(--s-6);
  border-top: 1px solid var(--petrol-2);
}
.footer__top {
  display: grid;
  gap: var(--s-7) var(--s-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
}
/* The brand column gets more room than the three link columns. */
@media (min-width: 62rem) {
  .footer__top { grid-template-columns: 1.6fr 1fr 1fr 1.4fr; }
}

.footer .logo { color: var(--text-inv); }
.footer .logo__text span { color: var(--mint); }
.footer__blurb {
  font-size: var(--fs-sm);
  line-height: 1.6;
  max-width: 32ch;
  color: var(--text-inv-2);
}

.footer__h {
  color: var(--mint);
  font-size: var(--fs-xs);
  margin-bottom: var(--s-4);
  font-weight: 500;
}

.footer__links { list-style: none; margin: 0; padding: 0; }
.footer__links li + li { margin-top: var(--s-2); }
.footer__links a {
  color: var(--text-inv-2);
  text-decoration: none;
  font-size: var(--fs-sm);
  /* Underline grows from the left on hover. underline-offset keeps it clear
     of the descenders so it does not look like a strikethrough. */
  background-image: linear-gradient(var(--mint), var(--mint));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1px;
  transition: background-size var(--dur-2) var(--ease-out), color var(--dur-2) var(--ease-out);
  padding-bottom: 2px;
}
.footer__links a:hover,
.footer__links a:focus-visible { color: var(--text-inv); background-size: 100% 1px; }

.footer__address {
  font-style: normal;
  font-size: var(--fs-sm);
  line-height: 1.7;
  color: var(--text-inv-2);
}

.footer__bottom {
  margin-top: var(--s-8);
  padding-top: var(--s-5);
  border-top: 1px solid var(--petrol-2);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3) var(--s-6);
  justify-content: space-between;
  align-items: baseline;
}
.footer__bottom .mono {
  font-size: var(--fs-xs);
  color: var(--text-inv-2);
  text-transform: none;
  letter-spacing: 0.02em;
}
.footer__bottom .mono:last-child { max-width: 44ch; }


/* --------------------------------------------------------------------------
   REDUCED MOTION — addendum for the components above.
   §09 already kills durations globally; these are the transform defaults
   that must be forced to their finished state.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .timeline__rule { transform: scaleY(1) !important; animation: none !important; }
  .timeline__rule::after { display: none; }
  .areas__list a::after { opacity: 1; translate: 0 0; }
  .footer__links a { background-size: 0 1px !important; }
}


/* ==========================================================================
   07d  ODDS AND ENDS
   ========================================================================== */
.hero__copy { min-width: 0; }
.footer__brand { min-width: 0; }
.mt-2 { margin-top: var(--s-2); }
.mt-3 { margin-top: var(--s-3); }
.mt-8 { margin-top: var(--s-8); }
.mt-9 { margin-top: var(--s-9); }

/* Figures used for the technical illustrations. The frame has a fixed aspect
   ratio so the image reserves its space before it loads — zero layout shift,
   which is a third of the Core Web Vitals score. */
.figure { margin: 0; position: relative; }
.figure__frame {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--r-3);
  background: var(--paper-2);
}
.figure__frame img,
.figure__frame svg {
  display: block;
  width: 100%;
  height: auto;
}
.sect--dark .figure__frame { border-color: var(--line-dark); background: var(--petrol-3); }
.figure figcaption {
  margin-top: var(--s-3);
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-3);
  display: flex;
  gap: var(--s-3);
  align-items: baseline;
}
.sect--dark .figure figcaption { color: var(--text-inv-2); }

/* ---- MIXING REAL PHOTOS IN WITH THE LINE DRAWINGS ------------------------
   The site ships with line drawings. Nothing here is used yet — it exists so
   that when you DO have photographs, they drop in beside the drawings and the
   two read as one series instead of two unrelated styles.

   To add a photo, copy any existing <figure> and add the figure--photo class:

     <figure class="figure figure--photo" data-reveal>
       <div class="figure__frame" data-figure>
         <img src="assets/img/my-photo.jpg" width="900" height="600"
              loading="eager" decoding="async" alt="Describe what is happening.">
       </div>
       <figcaption><b>FIG. 11</b> Short caption</figcaption>
     </figure>

   Three things make the mix work, and all three are handled below:

     1. ANY photo size fits. The frame is locked to 3:2 and the photo is
        cropped to fill it, so a client's 4032x3024 phone snap and a 900x600
        drawing occupy exactly the same box. Without this, one stray photo
        changes the height of a whole row.
     2. Same frame, same hairline, same radius as the drawings.
     3. Same numbered FIG. caption. This is the real unifier — numbered plates
        make a photograph and a diagram read as the same document.

   Keep the width/height attributes matching your photo's real pixel size
   (that is what prevents layout shift while it loads); the crop is CSS's job,
   not the attributes'. */
.figure--photo .figure__frame { background: var(--paper-3); }
.figure--photo .figure__frame img {
  aspect-ratio: 3 / 2;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
.figure--photo.figure--wide .figure__frame img { aspect-ratio: 16 / 9; }
.figure--photo.figure--tall .figure__frame img { aspect-ratio: 4 / 5; }

/* A drawing and a photo side by side, as a spread. The second plate drops by
   a step so the two figcaptions sit on different baselines — offsetting one
   of them is what stops a pair looking like two boxes in a row. Same device
   as the proof band on the home page.

     <div class="plate-pair">
       <figure class="figure">…the drawing…</figure>
       <figure class="figure figure--photo">…the photo…</figure>
     </div> */
.plate-pair { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-7); align-items: start; }
@media (min-width: 62rem) {
  .plate-pair { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-8) var(--s-7); }
  .plate-pair > :nth-child(2) { margin-top: var(--s-8); }
}
.figure figcaption b {
  font-family: var(--ff-mono);
  font-weight: 500;
  color: var(--oxide);
  flex: none;
}

/* ---- PROOF BAND -----------------------------------------------------------
   The first pictures on the page, sitting between the hero and the numbers.

   MOBILE (default, because that is the majority of this site's traffic):
   one column, and the technician figure is pulled ABOVE the copy. A visitor
   on a phone meets a picture roughly one scroll in instead of three, which
   was the whole reason this block exists.

   DESKTOP: the copy spans the full width as an intro, and the two figures
   sit side by side underneath it. The second figure drops by a step so the
   pair reads as a spread rather than two boxes in a row — offsetting only
   one of them puts the figcaptions on different baselines, and that is the
   detail that stops it looking like a stock template. */
.proof__grid {
  margin-top: var(--s-7);
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-7);
  align-items: start;
}
.proof__a { order: -1; }

@media (min-width: 62rem) {
  .proof__grid { grid-template-columns: 1fr 1fr; gap: var(--s-8) var(--s-7); }
  .proof__copy { grid-column: 1 / -1; }
  .proof__a { order: 0; }
  .proof__b { margin-top: var(--s-8); }
}

/* The diagnostic artwork is dark, so it needs the dark frame even though it
   sits on a paper section — a paper-coloured fill would read as a halo. */
.proof__b .figure__frame {
  border-color: var(--petrol);
  background: var(--petrol-3);
}


/* ==========================================================================
   07e  INNER-PAGE COMPONENTS
   --------------------------------------------------------------------------
   Used by services / pricing / service-areas / about / contact /
   privacy-policy / terms. It all reuses the same tokens as everything above,
   so this is not a second design system — just more of the first one.
   ========================================================================== */

/* ---- PAGE HEAD ------------------------------------------------------------
   Every inner page opens the same way: section marker, h1, standfirst. It is
   deliberately NOT the home hero — no graph paper, no illustration, no drum.
   An inner page that shouts as loudly as the home page makes a site feel like
   seven home pages stapled together. */
.page-head { padding-block: var(--s-8) var(--s-7); }
.page-head .display { font-size: clamp(2.1rem, 1.2rem + 3.6vw, 3.6rem); }
@media (min-width: 62rem) {
  .page-head { padding-block: var(--s-9) var(--s-8); }
}

/* ---- SPEC LIST ------------------------------------------------------------
   A <dl> styled as an engineering spec block: mono label, value under it,
   hairline between rows. Brands / price / warranty on services, opening
   hours on contact. */
.specs { display: grid; gap: 0; }
.specs dt {
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-3);
  padding-top: var(--s-4);
  border-top: 1px solid var(--hairline);
}
.specs dt:first-of-type { padding-top: 0; border-top: 0; }
.specs dd { margin: 0 0 var(--s-4); padding-top: 0.3rem; color: var(--text); }
.specs dd:last-of-type { margin-bottom: 0; }
.specs__price {
  font-family: var(--ff-display);
  font-size: var(--fs-h3);
  font-weight: 600;
  font-stretch: calc(var(--wd-title) * 1%);
  color: var(--oxide);
  line-height: 1.1;
}
.sect--dark .specs dt { border-top-color: var(--line-dark); color: var(--text-inv-2); }
.sect--dark .specs dd { color: var(--text-inv); }

/* ---- TICK LIST ------------------------------------------------------------
   Two borders rotated 45deg, not an SVG or a glyph: it scales with the text,
   inherits nothing it should not, and costs no extra request. */
.ticks { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s-3); }
.ticks > li { position: relative; padding-left: 1.6rem; }
.ticks > li::before {
  content: "";
  position: absolute;
  left: 0.1rem;
  top: 0.44em;
  width: 0.62rem;
  height: 0.34rem;
  border-left: 2px solid var(--oxide);
  border-bottom: 2px solid var(--oxide);
  transform: rotate(-45deg);
}
.sect--dark .ticks > li::before { border-color: var(--mint); }

/* ---- MONO SUB-HEADING ----------------------------------------------------
   For places that need a real heading element for document structure but
   where a display face would out-shout the h2 above it. */
.h-mono {
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-3);
}
.sect--dark .h-mono { color: var(--mint); }
.h4 { font-size: var(--fs-h4); font-weight: 600; font-stretch: calc(var(--wd-normal) * 1%); }

/* ---- SERVICES JUMP STRIP ------------------------------------------------- */
.svc-jump { gap: var(--s-4); }
.svc-jump .panel { padding: var(--s-5); display: grid; gap: var(--s-2); align-content: start; }
.svc-jump .panel > h3 { font-size: var(--fs-h4); }
.svc-jump .panel > p { color: var(--oxide); }

/* A hairline above each machine block after the first, so the four read as a
   series rather than four unrelated sections. */
.machine-block + .machine-block > .container {
  border-top: 1px solid var(--hairline);
  padding-top: var(--s-8);
}

/* ---- AREA CARDS ---------------------------------------------------------- */
.area-grid { gap: var(--s-5); }
.area-card { display: grid; gap: var(--s-3); align-content: start; }
.area-card > h3 { font-size: var(--fs-h4); }
.area-card__meta {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--oxide);
}
/* :target marks the card the visitor was sent to. They clicked "Koramangala"
   on another page; this is how they know which of twenty-four cards that was.
   scroll-margin-top keeps it out from under the sticky header. */
.area-card { scroll-margin-top: calc(var(--header-h) + var(--s-5)); }
.area-card:target { border-color: var(--oxide); box-shadow: inset 3px 0 0 var(--oxide); }

/* ---- ARROW LINK ---------------------------------------------------------- */
.link-arrow {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  text-decoration: none;
  color: var(--oxide);
}
.link-arrow::after {
  content: "\2192";
  transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1);
}
.link-arrow:hover::after,
.link-arrow:focus-visible::after { transform: translateX(0.28rem); }

/* ---- PROSE --------------------------------------------------------------- */
/* Nothing to add — .prose is already defined in §04 TYPOGRAPHY (68ch measure,
   spaced paragraphs) and the legal pages use it exactly as the home page does.
   Noted here only so nobody adds a second, conflicting .prose rule. */

/* ---- CONTACT ------------------------------------------------------------- */
.contact__big {
  font-family: var(--ff-display);
  font-size: var(--fs-h3);
  font-weight: 600;
  font-stretch: calc(var(--wd-title) * 1%);
  line-height: 1.15;
  margin-top: var(--s-3);
}
.contact__big a {
  color: var(--text);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  word-break: break-word;
}
.contact__big a:hover { color: var(--oxide); }

/* ---- CTA BAND ------------------------------------------------------------ */
.cta-band { padding-block: var(--s-9); }


/* ==========================================================================
   10  MOBILE REFINEMENT
   --------------------------------------------------------------------------
   Most visitors to a washing-machine repair site are on a phone, usually
   standing next to a broken washing machine. So this block is not an
   afterthought — it is the primary layout, and it gets its own pass.

   The desktop layout above already reflows correctly; what follows fixes the
   things that reflow *correctly but ugly*:

     - Vertical rhythm compresses. 6rem of section padding is generous on a
       27" display and absurd on a 375px one.
     - Optical margins tighten. A 24px gutter that frames the content on
       desktop just wastes a phone's narrow width.
     - Anything that became a single stretched column gets a real treatment
       rather than a full-bleed card.
     - Every tap target reaches 44px, per WCAG 2.5.8.
     - The sticky action bar gets clearance so it never covers the footer.
   ========================================================================== */

/* ---- 10.1  Tablet and below (< 62rem / 992px) ------------------------- */
@media (max-width: 61.999rem) {
  .rail { gap: var(--s-7); }
  /* A hairline between the rail's heading and its content, so the stacked
     order still reads as two distinct parts rather than one long column. */
  .rail > :first-child { padding-bottom: var(--s-6); border-bottom: 1px solid var(--hairline); }
  .sect--dark .rail > :first-child { border-bottom-color: var(--line-dark); }

  /* -- Illustration first ----------------------------------------------
     On desktop a section reads heading + specs | prose, with its figure as a
     full-width plate underneath. Stacked on a phone that plate ends up a
     screen and a half below the section marker — you scroll past the whole
     fault list before you see the machine you came to look at.

     So on narrow screens the figure is promoted above the rail. The section
     then opens marker → picture → heading, which is a stronger read anyway:
     the illustration introduces the block instead of summarising it.

     Add `figure-first` to any <section> that should behave this way. The
     machine blocks on services.html get it automatically.

     .container is only made a grid so `order` applies to its children. One
     column, no gap, so nothing else about the layout changes. */
  .machine-block > .container,
  .figure-first > .container { display: grid; grid-template-columns: minmax(0, 1fr); }

  .machine-block > .container > .marker,
  .figure-first > .container > .marker { order: -2; }

  .machine-block > .container > .figure,
  .figure-first > .container > .figure {
    order: -1;
    margin-top: 0;              /* overrides the .mt-8 in the markup */
    margin-bottom: var(--s-7);
  }

  /* The rail's hairline is redundant once the figure sits above it and the
     heading is the first text in the block. */
  .machine-block .rail > :first-child,
  .figure-first .rail > :first-child { padding-bottom: 0; border-bottom: 0; }
}

/* ---- 10.2  Phone (< 48rem / 768px) ----------------------------------- */
@media (max-width: 47.999rem) {

  :root {
    /* Tighter gutter, tighter sections. These two variables do most of the
       work — everything downstream is expressed in terms of them. */
    --gutter: 1.25rem;
    --section-y: 3.5rem;
  }

  /* The action bar is fixed to the bottom on phones, so the document needs
     matching clearance or it hides the last line of the footer. */
  body { padding-bottom: calc(var(--action-bar-h, 4rem) + env(safe-area-inset-bottom, 0px)); }

  /* -- Hero ------------------------------------------------------------
     The illustration moves BELOW the copy and is capped in height. A
     500px-tall SVG above the fold pushes the headline and the phone number
     off screen, which is the single most expensive mobile mistake a service
     site can make. */
  .hero { padding-block: var(--s-7) var(--s-8); }
  .hero__inner { gap: var(--s-6); }
  /* Recentre the mask. At desktop widths the off-centre 62% origin puts the
     graph-paper wash behind the illustration; on a single stacked column that
     same origin leaves a visible vertical seam down the middle of the page.
     Centring it turns the seam into an even wash behind the headline. */
  .hero__layer {
    background-size: 40px 40px;
    mask-image: radial-gradient(125% 55% at 50% 6%, #000 28%, transparent 82%);
  }

  /* Trust chips two-up. Four full-width rows is 145px of near-empty page;
     two-up is 74px and reads as a spec block rather than a list. */
  .hero__chips {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s-2);
    margin-top: var(--s-5);
  }
  .hero__chips .chip { width: 100%; font-size: 0.75rem; padding: var(--s-2) var(--s-3); }
  .hero__chips .chip svg { width: 0.85rem; height: 0.85rem; }
  .machine { max-width: 17rem; margin-inline: auto; }
  .hero__eyebrow { margin-bottom: var(--s-4); }

  /* Buttons go full width and stack. Two half-width buttons side by side at
     360px produces two-line labels, which looks broken. */
  .hero__cta { display: grid; gap: var(--s-3); }
  .hero__cta .btn { width: 100%; }

  /* -- Section headers -------------------------------------------------- */
  .marker { margin-bottom: var(--s-5); }
  .marker__rule { display: none; }   /* no room for it; the number carries it */

  /* -- Panels ----------------------------------------------------------- */
  .panel { padding: var(--s-5); }
  .grid { gap: var(--s-4); }

  /* Service cards go HORIZONTAL rather than staying as tall stacked blocks.
     Four identical full-width vertical cards is the "cheap template" look the
     brief rules out, and a two-up grid at 375px leaves 160px per card, which
     is not enough for a heading plus a price. So: icon in a left column,
     copy in the middle, price pinned bottom-right on its own baseline. Same
     information, a quarter of the scroll distance, and it reads as a
     designed row instead of a stretched card. */
  .svc-grid { grid-template-columns: 1fr; gap: var(--s-3); }
  .svc-grid .panel {
    padding: var(--s-4);
    display: grid;
    grid-template-columns: 2.5rem 1fr;
    grid-template-areas: "icon body" ".    price";
    gap: 0 var(--s-4);
    align-items: start;
  }
  .svc-grid .svc__icon {
    grid-area: icon;
    width: 2.5rem; height: 2.5rem;
    margin-bottom: 0;
  }
  .svc-grid .svc__icon svg { width: 1.2rem; height: 1.2rem; }
  .svc-grid .panel > h3 { grid-area: body; font-size: var(--fs-md); }
  .svc-grid .panel > p  { grid-area: body; font-size: var(--fs-sm); align-self: end; margin-top: 1.9em; }
  .svc-grid .svc__price {
    grid-area: price;
    margin-top: var(--s-4);
    padding-top: var(--s-3);
    align-items: baseline;
  }
  /* .panel--link draws its offset "print register" edge with ::after, which
     is absolutely positioned to inset:0 and so is unaffected by the grid
     areas above. Nothing to adjust. */

  /* -- Trust band ------------------------------------------------------- */
  .band__grid { grid-template-columns: 1fr 1fr; gap: var(--s-5) var(--s-4); }

  /* -- Symptom picker --------------------------------------------------- */
  .sym__grid { grid-template-columns: 1fr 1fr; gap: var(--s-3); }
  .sym { padding: var(--s-4) var(--s-3); min-height: 5.5rem; }
  .sym__label { font-size: var(--fs-sm); }

  /* -- Pricing table ---------------------------------------------------- */
  .pricing__head { display: block; }
  .pricing__head > * + * { margin-top: var(--s-5); }
  /* The scroll hint has to be visible or people simply do not discover that
     the table scrolls sideways. A right-edge fade plus a mono caption is
     enough; a JS "swipe me" toast is not worth the weight. */
  .table-wrap {
    position: relative;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
  }
  .table-wrap::after {
    content: "";
    position: absolute; inset-block: 0; right: 0; width: 2.5rem;
    pointer-events: none;
    background: linear-gradient(to right, transparent, var(--paper) 78%);
  }
  .table-wrap[data-scrolled-end]::after { opacity: 0; }
  .sect--inset .table-wrap::after { background: linear-gradient(to right, transparent, var(--paper-2) 78%); }
  .table th:first-child, .table td:first-child { scroll-snap-align: start; }
  .table { font-size: var(--fs-sm); min-width: 34rem; }
  .table th, .table td { padding: var(--s-3) var(--s-3); }
  .pricing__notes { grid-template-columns: 1fr; gap: var(--s-2); }
  .table-hint { display: flex; }

  /* -- Timeline --------------------------------------------------------- */
  .timeline { gap: var(--s-6); }
  .timeline__body h3 { font-size: var(--fs-md); }

  /* -- Testimonials ----------------------------------------------------- */
  .quote blockquote p { font-size: var(--fs-body); }

  /* -- Areas ------------------------------------------------------------ */
  .areas__list { columns: 1; }
  .areas__list a { padding-block: var(--s-3); min-height: 2.75rem; align-items: center; }
  /* The hover arrow is meaningless on touch, so show it permanently — it
     then reads as an affordance instead of a hover artefact. */
  .areas__list a::after { opacity: 0.45; translate: 0 0; }

  /* -- Booking ---------------------------------------------------------- */
  .book__form { padding: var(--s-5) var(--s-4); }
  /* 16px minimum, or iOS Safari zooms the whole page in on focus and the
     visitor has to pinch back out. This is the single most common mobile
     form bug on the web. */
  .input, .select, .textarea { font-size: 1rem; }
  .textarea { min-height: 6.5rem; }

  /* -- Marquee ---------------------------------------------------------- */
  .brands { padding-block: var(--s-7); }
  .brands .marquee__item { font-size: var(--fs-body); padding-inline: var(--s-4); }

  /* -- Footer ----------------------------------------------------------- */
  .footer { padding-block: var(--s-8) var(--s-6); }
  .footer__top { gap: var(--s-6); grid-template-columns: 1fr 1fr; }
  .footer__brand { grid-column: 1 / -1; }
  .footer__top > :nth-child(4) { grid-column: 1 / -1; }
  .footer__links a { display: inline-flex; align-items: center; min-height: 2.5rem; }
  .footer__bottom { margin-top: var(--s-6); gap: var(--s-2); }
  .footer__bottom .mono:last-child { max-width: none; }

  /* -- Accordion -------------------------------------------------------- */
  .acc__item > summary { padding: var(--s-4) 0; gap: var(--s-4); }
  .acc__q { font-size: var(--fs-body); }

  /* -- Figures ---------------------------------------------------------- */
  .figure figcaption { font-size: 0.6875rem; letter-spacing: 0.06em; }

  /* -- Inner-page components (§07e) ------------------------------------- */

  /* Page heads lose a step of air. There is no illustration competing with
     the h1 here, so the extra padding only pushes content down. */
  .page-head { padding-block: var(--s-7) var(--s-6); }

  /* Spec lists go two-up: label left, value beside it, one row each. Stacked
     at full width a four-row <dl> eats half a screen for eight short strings.

     column-gap is 0 and the gutter is dd's padding-left instead, so the two
     cells' top hairlines meet and draw as ONE continuous rule across the row.
     With a gap the rule breaks in the middle and the whole block looks
     unfinished. */
  .specs {
    grid-template-columns: minmax(6.5rem, auto) 1fr;
    column-gap: 0;
    align-items: baseline;
  }
  .specs dt { padding-top: var(--s-3); padding-right: var(--s-4); }
  .specs dd {
    margin-bottom: 0;
    padding-top: var(--s-3);
    padding-left: var(--s-4);
    border-top: 1px solid var(--hairline);
  }
  .specs dd:first-of-type { border-top: 0; padding-top: 0; }
  .sect--dark .specs dd { border-top-color: var(--line-dark); }

  /* The jump strip becomes a two-up index rather than four stacked cards —
     it is navigation, and navigation should not be a screen tall. The labels
     are two words each, so they survive a 160px column.

     minmax(0, ...) not 1fr: a bare 1fr means minmax(AUTO, 1fr), and an auto
     minimum is min-content — so a long word inside can push the track wider
     than its share and overflow the grid. minmax(0, 1fr) is the fix. */
  .svc-jump { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-3); }
  .svc-jump .panel { padding: var(--s-4); min-height: 44px; }
  .svc-jump .panel > h3 { font-size: var(--fs-body); }

  .machine-block + .machine-block > .container { padding-top: var(--s-7); }

  /* Area cards stay ONE column on a phone. Two was tried and rejected: each
     heading is a full sentence ("Washing machine repair in Electronic City"),
     which wraps to four cramped lines in a 160px track. A long scannable
     list beats a short unreadable grid — and most people arrive here from an
     anchor link anyway, which jumps them straight to their card. */
  .area-grid { grid-template-columns: minmax(0, 1fr); gap: var(--s-4); }
  .area-card { padding: var(--s-5); gap: var(--s-2); }
  .area-card > h3 { font-size: var(--fs-h4); }
  .area-card__meta { font-size: 0.6875rem; letter-spacing: 0.05em; }

  /* Tap targets. .link-arrow is a real link in body copy, so it needs the
     44px height even though it looks like a caption. */
  .link-arrow { min-height: 44px; align-items: center; }

  .cta-band { padding-block: var(--s-8); }

  /* The phone number and email are the two things people actually come to
     contact.html for — they get the display face but must not overflow. */
  .contact__big { font-size: var(--fs-h4); }
}

/* ---- 10.3  Small phone (<= 374px) ------------------------------------ */
/* Stated in px, not rem, on purpose: the cut has to fall precisely BELOW
   375px (iPhone SE / 12 mini / 13 mini) so that band keeps the full 10.2
   treatment. 375px is the commonest viewport this site will ever be opened
   at and must not be handled as a narrow edge case. Only 320-374px lands
   here — old SEs and a few small Androids. */
@media (max-width: 374px) {
  :root { --gutter: 1rem; }
  .sym__grid, .footer__top { grid-template-columns: 1fr; }
  .machine { max-width: 13rem; }
  .table { min-width: 28rem; }
  .svc-grid .panel { grid-template-columns: 2.25rem 1fr; gap: 0 var(--s-3); }
  /* The jump strip drops to one column too — two words in an 140px track
     still wrap, and a wrapped nav label looks like a mistake. */
  .svc-jump { grid-template-columns: minmax(0, 1fr); }
  /* And the spec rows go back to stacked, because a 6.5rem label column plus
     a value leaves nothing usable at 320px. */
  .specs { grid-template-columns: 1fr; }
  .specs dt { padding-right: 0; }
  .specs dd { padding-left: 0; border-top: 0; padding-top: 0.3rem; margin-bottom: var(--s-4); }
  .specs dd:last-of-type { margin-bottom: 0; }
}

/* ---- 10.4  Landscape phone ------------------------------------------- */
/* Short viewport: kill the generous hero padding so something other than
   the headline is visible. */
@media (max-height: 30rem) and (orientation: landscape) {
  .hero { padding-block: var(--s-6); }
  .machine { max-width: 12rem; }
  .action-bar { display: none; }
}

/* ---- 10.5  Touch devices --------------------------------------------- */
/* Hover-dependent affordances are re-expressed as always-on, and the lift
   transforms are dropped so nothing sticks in a hovered state after a tap. */
@media (hover: none) {
  .panel--link:hover { transform: none; }
  .footer__links a { background-size: 0 1px; }
  .nav__link::after { transform: scaleX(0); }
}
