@font-face {
  font-family: 'Archivo';
  src: url('/assets/fonts/archivo.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Big Shoulders';
  src: url('/assets/fonts/bigshoulders.woff2') format('woff2');
  font-weight: 400 800;
  font-style: normal;
  font-display: swap;
}

/* ---------------------------------------------------------------- tokens */
:root {
  --ink: #0b0d0f;
  --asphalt: #14181b;
  --steel: #1e262a;
  --line-dk: #2d383d;
  --paper: #e8e5de;
  --paper-2: #dcd8cf;
  --white: #fbfaf8;
  /* the three blues are all sampled off logo-shannos-v4.webp, which runs a
     gradient down the lettering from a deep blue at the edges through the
     bright electric blue in the middle. --hi is the body of that gradient and
     is the brand colour: it carries white at 6.3:1 so it is what solid fills,
     buttons and rules are made of, and it holds 5.0:1 against the cream bands.
     On the near-black grounds it only reaches 3.1:1, which is not enough for a
     label or a link, so text and small marks on black take --hi-lt, the bright
     band from the middle of the lettering, at 6.6:1. --hi-dk is the darkest
     part of the same lettering and is the hover on the solid fills. */
  --hi: #0650e9;
  --hi-lt: #0c9bfc;
  --hi-dk: #053dc3;
  /* the chrome off the MECH lettering, for the outlined buttons and the
     secondary text on the dark grounds */
  --chrome: #b0b0af;
  /* these two were the navy bands. They are charcoal now, so the only blue on
     the site is the logo blue: --petrol a step lighter than --asphalt so a
     call band still reads as its own band against the one above it, --petrol-dk
     a step darker than --ink for the page headers. */
  --petrol: #212a2e;
  --petrol-dk: #101417;
  --grey-dk: #4a5155;
  --grey-lt: #b0b0af;

  --fd: 'Big Shoulders', 'Arial Narrow', Impact, sans-serif;
  --fb: 'Archivo', 'Helvetica Neue', Arial, sans-serif;

  --pad: clamp(1.15rem, 4vw, 2.5rem);
  --gutter: clamp(1.5rem, 4vw, 3.5rem);
  --band: clamp(3.5rem, 8vw, 7rem);
}

/* ----------------------------------------------------------------- reset */
*, *::before, *::after { box-sizing: border-box; }
/* the masthead is sticky at 70px, so anchored sections need clearing or the
   section's eyebrow label finishes underneath it */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: 90px; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--fb);
  font-size: 1.0625rem;
  line-height: 1.62;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }
button { font: inherit; }
h1, h2, h3, h4 { margin: 0; font-weight: 700; }
h1 + p, h2 + p, h3 + p { margin-top: .95rem; }
p { margin: 0 0 1.05em; }
ul { margin: 0; padding: 0; list-style: none; }

::selection { background: var(--hi); color: var(--white); }

:focus-visible {
  outline: 3px solid var(--hi);
  outline-offset: 2px;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--hi); color: var(--white); padding: .9rem 1.25rem;
  font-weight: 700;
}
.skip:focus { left: 0; }

/* ---------------------------------------------------------------- layout */
.wrap {
  width: 100%;
  max-width: 1250px;
  margin-inline: auto;
  padding-inline: var(--pad);
}
/* the tight column used to narrow the whole band, which put the first section
   of the five photo-less service pages on a different left edge from every
   band under it. The box keeps the .wrap width and the measure is held on the
   content instead, so one page only ever has one left edge. */
.wrap--tight > * { max-width: 880px; }

.band { padding-block: var(--band); }
.band--sm { padding-block: clamp(2.5rem, 5vw, 4rem); }
/* the eight service pages run as type on cream with no photograph in the body,
   and at 1440px the full band padding left about 160px of dead ground between
   the end of the checklist and the "Gear we cover" eyebrow under it. The
   yellow blocks come up to close it. One rule, all eight pages, including the
   ones nobody has reported. */
.band--tail { padding-bottom: clamp(1.5rem, 2.5vw, 2rem); }

.dark { background: var(--ink); color: var(--white); }
.asphalt { background: var(--asphalt); color: var(--white); }
.petrol { background: var(--petrol); color: var(--white); }
.paper { background: var(--paper); color: var(--ink); }
.paper-2 { background: var(--paper-2); color: var(--ink); }
/* the two beiges are a shade apart, so where one band of each ran straight into
   the next the join came out as a hard line across the page: too close together
   to read as a change of band, too far apart to read as one ground, and the
   only transition on the site not marked by the tape. Where they meet, the
   second band takes the first one's ground and the pair reads as one continuous
   surface, which is what the eyebrow at the top of the second band is already
   there to divide. One rule, every join, including the ones on the seven
   service pages that nobody has reported yet. */
.paper + .paper-2 { background: var(--paper); }
.paper-2 + .paper { background: var(--paper-2); }
.hi { background: var(--hi); color: var(--white); }

/* the yellow and black hazard tape is gone. Every join it used to mark has a
   band of one colour on one side and a band of another on the other, so the
   change of ground does the dividing on its own and the divider draws nothing.
   The elements stay in the markup because the background rules above are
   written on adjacent siblings, and taking them out would change which bands
   those rules match. */
.hazard, .hazard--thin, .hazard--inv {
  height: 0;
  background: none;
}
/* the one join with no change of ground to read is a near-black band running
   into the near-black footer, which is what this variant was always for: it is
   on our-work, breakdowns, mobile-service and 404. That one keeps a plain
   hairline so the footer still starts somewhere. */
.hazard--ondark {
  height: 1px;
  background: var(--line-dk);
}

/* ------------------------------------------------------------ typography */
/* .87 was tight enough that a comma at the end of a line landed inside the
   cap height of the line below it. That was patched at phone width, but any
   narrow column does the same thing, so .93 is now the one value everywhere
   rather than a breakpoint's worth of exception. */
.d1, .d2, .d3, .d4 {
  font-family: var(--fd);
  font-weight: 800;
  text-transform: uppercase;
  line-height: .93;
  letter-spacing: .004em;
  text-wrap: balance;
}
.d1 { font-size: clamp(2.9rem, 8.4vw, 5.6rem); }
.d2 { font-size: clamp(2.3rem, 5.6vw, 3.9rem); }
.d3 { font-size: clamp(1.7rem, 3.4vw, 2.5rem); }
.d4 { font-size: clamp(1.35rem, 2.3vw, 1.7rem); line-height: .95; }

.eyebrow {
  font-size: .755rem;
  font-weight: 700;
  letter-spacing: .19em;
  text-transform: uppercase;
  color: var(--grey-dk);
  display: flex;
  align-items: center;
  gap: .7rem;
  margin: 0 0 1.15rem;
}
.eyebrow::before {
  content: "";
  width: 30px; height: 4px;
  background: var(--hi);
  flex: none;
}
.dark .eyebrow, .petrol .eyebrow, .asphalt .eyebrow { color: var(--hi-lt); }
.dark .eyebrow::before, .petrol .eyebrow::before, .asphalt .eyebrow::before { background: var(--hi-lt); }
.hi .eyebrow { color: var(--ink); }
.hi .eyebrow::before { background: var(--ink); }

.lede {
  font-size: clamp(1.08rem, 1.6vw, 1.28rem);
  line-height: 1.5;
  max-width: 46ch;
  color: var(--grey-dk);
}
.dark .lede, .asphalt .lede, .petrol .lede { color: #cfd6d9; }

.muted { color: var(--grey-dk); }
.dark .muted, .asphalt .muted, .petrol .muted { color: var(--grey-lt); }

.body-lg { font-size: 1.08rem; }

/* --------------------------------------------------------------- buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .6rem;
  min-height: 54px;
  padding: .9rem 1.5rem;
  font-family: var(--fb);
  font-weight: 700;
  font-size: .9rem;
  letter-spacing: .085em;
  text-transform: uppercase;
  text-decoration: none;
  border: 2px solid transparent;
  border-radius: 0;
  cursor: pointer;
  transition: background-color .18s ease, color .18s ease, border-color .18s ease, transform .18s ease;
}
.btn:hover { transform: translateY(-2px); }
.btn--hi { background: var(--hi); color: var(--white); }
.btn--hi:hover { background: var(--hi-dk); }
.btn--ink { background: var(--ink); color: var(--white); }
.btn--ink:hover { background: var(--steel); }
.btn--ghost { border-color: var(--chrome); color: var(--white); }
.btn--ghost:hover { border-color: var(--hi-lt); color: var(--hi-lt); }
.btn--ghost-dk { border-color: rgba(11,13,15,.35); color: var(--ink); }
.btn--ghost-dk:hover { border-color: var(--ink); background: var(--ink); color: var(--white); }
.btn svg { width: 17px; height: 17px; flex: none; }

.btn-row { display: flex; flex-wrap: wrap; gap: .8rem; }
@media (max-width: 520px) {
  .btn-row .btn { width: 100%; }
}

.tel-line {
  font-family: var(--fd);
  font-weight: 800;
  font-size: clamp(2.1rem, 5.5vw, 3.1rem);
  letter-spacing: .01em;
  text-decoration: none;
  display: inline-block;
  line-height: 1;
  padding-block: .55rem;
}
.tel-line:hover { color: var(--hi-lt); }
/* the number sits on a beige band on the home enquiry section, where yellow
   on beige is not a hover state anybody can read */
.paper .tel-line:hover, .paper-2 .tel-line:hover { color: var(--grey-dk); }

/* ---------------------------------------------------------------- header */
/* the strip says the same thing the hero eyebrow says 170px further down, and
   on a phone it wraps onto two lines to say it: 49px of the first screen spent
   on a duplicate, on top of a 71px sticky masthead. It only earns its place
   once the address half of it is showing as well, so it runs from 900px and is
   off below that. Every page, rather than a breakpoint's worth of exception. */
.topbar {
  display: none;
  background: #000;
  color: #9aa2a6;
  font-size: .73rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  font-weight: 600;
}
@media (min-width: 900px) { .topbar { display: block; } }
.topbar__in {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  min-height: 34px;
  padding-block: .35rem;
}
/* the strip is wider than a 320px phone, and holding it on one line pushed the
   whole document sideways, so it is allowed to wrap */
.topbar__in span { white-space: normal; }
@media (min-width: 620px) { .topbar__in span { white-space: nowrap; } }
.topbar b { color: var(--hi-lt); font-weight: 700; }
.topbar__r { display: none; }
@media (min-width: 860px) { .topbar__r { display: block; } }

.masthead {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--ink);
  border-bottom: 1px solid var(--line-dk);
}
.masthead__in {
  display: flex;
  align-items: center;
  gap: .7rem;
  min-height: 70px;
  padding-block: .6rem;
}
/* the logo is shorter than the buttons beside it, so give it the same box
   height and centre it, otherwise the header reads as two rows */
.brand { flex: none; display: flex; align-items: center; min-height: 48px; line-height: 0; }
.brand img { width: 124px; }
@media (min-width: 480px) { .brand img { width: 152px; } }
@media (min-width: 900px) { .brand img { width: 196px; } }

.nav { display: none; margin-left: auto; }
@media (min-width: 1000px) {
  .nav { display: flex; align-items: center; gap: 1.6rem; }
}
.nav a {
  font-size: .795rem;
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: #d5dadd;
  text-decoration: none;
  padding: .55rem 0;
  border-bottom: 3px solid transparent;
}
.nav a:hover { color: var(--hi-lt); }
.nav a[aria-current="page"] { color: var(--white); border-bottom-color: var(--hi-lt); }

.head-cta {
  margin-left: auto;
  display: flex;
  gap: .5rem;
  align-items: center;
}
@media (min-width: 1000px) { .head-cta { margin-left: 1.6rem; } }
.head-cta .btn {
  min-height: 48px;
  padding: .6rem 1rem;
  font-size: .84rem;
  letter-spacing: .04em;
}
.head-cta .btn--quote { display: none; }
@media (min-width: 1180px) { .head-cta .btn--quote { display: inline-flex; } }
.head-cta .btn--hi { padding-inline: .8rem; letter-spacing: .02em; font-size: .82rem; }
@media (min-width: 480px) { .head-cta .btn--hi { padding-inline: 1rem; font-size: .86rem; } }

.burger {
  flex: none;
  width: 48px; height: 48px;
  display: grid;
  place-content: center;
  gap: 5px;
  background: transparent;
  border: 1px solid var(--line-dk);
  color: var(--white);
  cursor: pointer;
}
@media (min-width: 1000px) { .burger { display: none; } }
.burger span {
  display: block;
  width: 21px; height: 2px;
  background: currentColor;
}

.mobnav {
  display: none;
  background: var(--asphalt);
  border-bottom: 1px solid var(--line-dk);
}
.mobnav.is-open { display: block; }
@media (min-width: 1000px) { .mobnav.is-open { display: none; } }
.mobnav.is-open { padding-bottom: 1.4rem; }
.mobnav ul { padding: .6rem 0 1.4rem; }
.mobnav ul a {
  display: block;
  padding: .95rem 0;
  border-bottom: 1px solid var(--line-dk);
  font-family: var(--fd);
  font-weight: 700;
  font-size: 1.55rem;
  text-transform: uppercase;
  letter-spacing: .01em;
  color: var(--white);
  text-decoration: none;
}
.mobnav ul a:hover, .mobnav ul a[aria-current="page"] { color: var(--hi-lt); }
.mobnav .btn { width: 100%; margin-top: 1.1rem; }
/* below 1000px the only navigation is the burger, and the burger only does
   anything once site.js has run: with the script blocked or broken both .nav
   and #mobnav compute to display:none and a phone has no menu on the page at
   all. The document ships as html.no-js and site.js swaps that class as its
   first statement, so until it does the menu renders open and the control that
   cannot work is not shown. The scripted path below 1000px is untouched. */
.no-js .mobnav { display: block; padding-bottom: 1.4rem; }
.no-js .burger { display: none; }
@media (min-width: 1000px) {
  .no-js .mobnav { display: none; }
}

/* ------------------------------------------------------------------ hero */
.hero {
  position: relative;
  background: var(--ink);
  color: var(--white);
  overflow: hidden;
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url('/assets/img/diesel-engine-v2.webp');
  background-size: cover;
  background-position: 30% 50%;
  opacity: .17;
  filter: grayscale(.4) contrast(1.05);
}
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, var(--ink) 18%, rgba(11,13,15,.88) 48%, rgba(11,13,15,.55) 100%);
}
.hero__in {
  position: relative;
  z-index: 1;
  padding-block: clamp(2.6rem, 6vw, 5rem) clamp(2.8rem, 6vw, 5.2rem);
  display: grid;
  gap: clamp(2rem, 4vw, 3.4rem);
}
@media (min-width: 950px) {
  .hero__in { grid-template-columns: minmax(0, 1.12fr) minmax(0, .88fr); align-items: center; }
}
.hero__kicker {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--hi-lt);
  margin: 0 0 1.25rem;
  display: flex;
  align-items: center;
  gap: .7rem;
}
.hero__kicker::before { content: ""; width: 34px; height: 4px; background: var(--hi-lt); flex: none; }
.hero h1 { margin: 0 0 1.2rem; }
.hero h1 em { font-style: normal; color: var(--hi-lt); }
/* hold the yellow phrase together on its own line once there is room for it,
   so the br only comes in at desktop. The source keeps a space in front of the
   br, or hiding it here runs "repairs" and "you" into one word on every phone */
.hero h1 br { display: none; }
@media (min-width: 950px) { .hero h1 br { display: inline; } }
.hero__sub {
  font-size: clamp(1.06rem, 1.7vw, 1.26rem);
  line-height: 1.55;
  color: #cbd2d6;
  max-width: 44ch;
  margin: 0 0 1.9rem;
}
.hero .btn-row { margin-bottom: 1.9rem; }

.hero__facts {
  display: flex;
  flex-wrap: wrap;
  gap: .55rem 1.5rem;
  font-size: .9rem;
  font-weight: 600;
  letter-spacing: .095em;
  text-transform: uppercase;
  color: #b7bfc3;
}
.hero__facts li { display: flex; align-items: center; gap: .65rem; }
/* the square used to be a separator drawn between items, which is fine on one
   line and falls apart the moment the row wraps: the item that drops to the
   second line arrives with a leading square the first item never had, indented
   further right than the line above it. At 1440 the three items do not fit on
   one line, so that was the state of the last thing in the first screen. Every
   item carries its own leading square instead, so the row reads the same
   whether it wraps or not, and there is one rule rather than a breakpoint's
   worth of exception. */
.hero__facts li::before {
  content: "";
  width: 6px; height: 6px;
  background: var(--hi-lt);
  display: block;
  flex: none;
  margin-right: .25rem;
}
/* at phone width the row breaks to three lines anyway, so it is set as a real
   stacked list */
@media (max-width: 620px) {
  .hero__facts { display: grid; gap: .45rem; }
}
/* the proof chips and the hours are the two lines a driveway visitor checks,
   so they run at the same size and the same grey as each other */
.hero__hours {
  margin: 1rem 0 0;
  font-size: .9rem;
  font-weight: 600;
  letter-spacing: .095em;
  text-transform: uppercase;
  color: #b7bfc3;
}

.hero__shot { position: relative; }
/* the frame is a true square and the shot in it is a square crop of the same
   photograph, taken off the 1536x2048 original rather than the 1065x734 one,
   so cover at centre has nothing left to trim and the ute keeps its tray, its
   bumper and both wheels. At 1440 the right column is about 490px wide and the
   square takes all of it. */
.hero__shot img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  object-position: 50% 50%;
  border: 1px solid rgba(255,255,255,.16);
}
/* the blue outline used to be a separate frame offset out of the picture on
   three sides. It is the picture's own border now, so it sits exactly on the
   photo's edges and nothing hangs outside the square. */
@media (min-width: 950px) {
  .hero__shot img { border: 2px solid var(--hi-lt); }
}

/* --------------------------------------------------------------- marquee */
.marquee {
  background: #000;
  border-block: 1px solid #23292c;
  overflow: hidden;
  padding-block: .85rem;
}
.marquee__track {
  display: flex;
  width: max-content;
  gap: 0;
  animation: slide 46s linear infinite;
}
.marquee__grp {
  display: flex;
  align-items: center;
  gap: 2.4rem;
  padding-right: 2.4rem;
  font-family: var(--fd);
  font-weight: 800;
  text-transform: uppercase;
  font-size: 1.5rem;
  letter-spacing: .05em;
  color: var(--hi-lt);
  white-space: nowrap;
}
.marquee__grp span:nth-child(even) { color: #575f63; }
@keyframes slide { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
  .marquee__track { animation: none; }
}

/* ------------------------------------------------------- equipment index */
.equip__head {
  display: grid;
  gap: 1.4rem;
  margin-bottom: 2.6rem;
}
@media (min-width: 880px) {
  .equip__head { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: end; gap: 3rem; }
}
.equip__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(155px, 1fr));
  gap: 0 2rem;
  border-top: 2px solid rgba(11,13,15,.75);
}
/* the items never divide evenly into the auto-fill columns, so a rule under
   each cell left every group ending on a half-drawn line with empty cells
   beside it. The spacing carries the rows instead. */
.equip__list li {
  padding: .72rem 0;
  font-size: .95rem;
  font-weight: 500;
  display: flex;
  align-items: baseline;
  gap: .55rem;
}
.equip__list li::before {
  content: "";
  width: 7px; height: 7px;
  background: var(--hi);
  border: 1px solid rgba(11,13,15,.35);
  flex: none;
  transform: translateY(-1px);
}
/* the brand blue is pitched to hold against the cream bands, which is where
   most of these lists are. The service area lists on about and on breakdowns
   run on a dark band, where the same square goes nearly black against the
   ground, so there it takes the bright blue and a light edge instead. */
.dark .equip__list li::before,
.asphalt .equip__list li::before,
.petrol .equip__list li::before {
  background: var(--hi-lt);
  border-color: rgba(255,255,255,.3);
}
/* the seven service areas ran as an inline wrapped line, so the rows broke two,
   three, two at 390px and four, three at 1440px and the list read as though it
   had landed where it fell. They sit on a fixed grid instead, three columns at
   phone width and four once there is room, so the rows line up. One rule, the
   service area list on about and on breakdowns. */
.equip__list--flow {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0 1.6rem;
}
@media (min-width: 1000px) {
  .equip__list--flow { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* twenty-odd near-identical rows in one grid stops being read about eight in,
   so the list is broken into labelled groups */
.equip__grp + .equip__grp { margin-top: 2.2rem; }
/* six full-width groups stacked one under another ran the band to two phone
   screens of small caps headings and tiny bullets, and the auto-fill columns
   left most rows half empty because no group divides into six. The groups run
   two and three up, each list in a single column, so every row fills and the
   band is about a third of the height it was. */
.equip__grid { display: grid; gap: 2.2rem; }
.equip__grid .equip__grp + .equip__grp { margin-top: 0; }
@media (min-width: 620px) {
  .equip__grid { grid-template-columns: 1fr 1fr; gap: 2.4rem 3rem; }
  /* once the groups sit side by side their items run one per row, so no row
     is ever part empty */
  .equip__grid .equip__list { grid-template-columns: minmax(0, 1fr); }
}
@media (min-width: 1000px) { .equip__grid { grid-template-columns: repeat(3, 1fr); } }
.equip__grp h3 {
  font-family: var(--fd);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(1.25rem, 2.2vw, 1.55rem);
  line-height: 1;
  margin-bottom: .9rem;
}
/* six lists set at one size read as a spec sheet with nothing to enter it by,
   so the three categories the business is built on lead the block and the
   three lighter ones follow at the size they were. */
.equip__grp--lead h3 { font-size: clamp(1.5rem, 2.9vw, 2.05rem); }
.equip__grp--lead .equip__list li { font-size: 1.05rem; }
/* one machine name per row, six groups of them, ran this band about 850px down
   the page at 1440px and 2,300px on a phone, and most of those rows were a
   four character word in a 358px column with a small yellow square in front of
   it. It reads as a spreadsheet and it is the point where the page goes dull.
   Under the rule each group already carries, the names run as one line of
   text: same list, same groups, same words, about a third of the height. It is
   scoped to the grouped catalogue, so the ruled list the service areas use on
   about and breakdowns is untouched. */
.equip__grid .equip__list {
  display: block;
  padding-top: .72rem;
  font-size: .95rem;
  font-weight: 500;
}
.equip__grid .equip__grp--lead .equip__list { font-size: 1.05rem; }
.equip__grid .equip__list li {
  display: inline;
  padding: 0;
  font-size: inherit;
}
.equip__grid .equip__list li::before { content: none; }
.equip__grid .equip__list li:not(:last-child)::after { content: ","; }
/* the three lead groups are a size larger so the block has somewhere to be
   entered, which works the moment the groups sit side by side. Stacked in one
   column below 620px the reader meets three big headings and then three small
   ones with nothing structural to say why, so the distinction waits until
   there are columns to make it in. */
@media (max-width: 619.98px) {
  .equip__grid .equip__grp--lead h3 { font-size: clamp(1.25rem, 2.2vw, 1.55rem); }
  .equip__grid .equip__grp--lead .equip__list { font-size: .95rem; }
}
.equip__foot {
  margin-top: 1.8rem;
  font-size: 1.05rem;
  font-weight: 500;
}
.equip__foot a { font-weight: 700; text-decoration-thickness: 2px; text-underline-offset: 3px; }

/* ----------------------------------------------------------- service rows */
/* the intro ran as a sticky left third against the six rows. It did track the
   scroll, but it is a quarter of the height of the list, so the left third of
   a 1440px page is empty beside almost all of it, and the column it left for
   the list squeezed the photographs down to 286px. The intro sits above the
   list at the measure the rest of the site sets its section intros at, and the
   rows take the full container, which is what gives the thumbnails room. */
.svc {
  display: grid;
  gap: 2rem;
}
.svc__aside { max-width: 54ch; }
.svc__rows { border-top: 2px solid currentColor; }
.svc__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: .9rem 1.2rem;
  align-items: start;
  padding: 1.35rem 0;
  border-bottom: 1px solid rgba(255,255,255,.16);
  text-decoration: none;
  color: inherit;
  transition: background-color .2s ease, padding .2s ease;
}
.paper .svc__row, .paper-2 .svc__row { border-bottom-color: rgba(11,13,15,.16); }
.svc__row:hover { background: rgba(12,155,252,.12); }
.paper .svc__row:hover, .paper-2 .svc__row:hover { background: rgba(11,13,15,.05); }
.svc__num {
  font-family: var(--fd);
  font-weight: 700;
  font-size: 1.45rem;
  color: var(--hi-lt);
  line-height: 1;
  padding-top: .18rem;
  min-width: 2.2rem;
}
.paper .svc__num, .paper-2 .svc__num { color: var(--ink); }
.svc__row h3 { margin-bottom: .3rem; }
.svc__row p { margin: 0; font-size: .97rem; color: var(--grey-lt); max-width: 62ch; }
.paper .svc__row p, .paper-2 .svc__row p { color: var(--grey-dk); }
.svc__go {
  font-size: 1.4rem;
  line-height: 1;
  align-self: center;
  transition: transform .2s ease;
  color: var(--hi-lt);
}
.paper .svc__go, .paper-2 .svc__go { color: var(--ink); }
.svc__row:hover .svc__go { transform: translateX(6px); }
/* at phone width the arrow sits in a gutter the copy runs straight into, and
   the whole row is the tap target anyway */
@media (max-width: 700px) {
  .svc__go { display: none; }
}

/* the services index carries a thumbnail per row, so it is not the home page
   list again with the colours swapped. The arrow the home list carries is
   dropped here: with a thumbnail in the row it ended up stranded a long way
   right of the copy it belonged to, so the heading takes the hover instead
   and the whole row is the link it always was. */
.svc__rows--thumbs .svc__row {
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "num copy" "thumb thumb";
}
.svc__rows--thumbs .svc__num { grid-area: num; }
.svc__rows--thumbs .svc__copy { grid-area: copy; }
.svc__rows--thumbs .svc__go { display: none; }
/* with the arrow dropped here, and dropped again under 700px, nothing in these
   rows said link except a hover tint, which a phone never shows: six links
   with no static cue at any width. The heading carries a permanent underline
   and the hover thickens it, so the row reads as a link before it is touched. */
.svc__rows--thumbs .svc__row h3 {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}
.svc__rows--thumbs .svc__row:hover h3,
.svc__rows--thumbs .svc__row:focus-visible h3 {
  text-decoration-thickness: 3px;
}
/* eight rows of number, thumbnail, heading and two lines, one straight after
   another, is where the services page stopped being read: one band running
   more than half the page with nothing changing down it but the words. The
   rows are broken into three labelled groups, marked with the same eyebrow the
   rest of the site marks a section with, so the list can be entered anywhere
   rather than only at the top. */
.svc__grp { margin: 2.2rem 0 1rem; }
.svc__grp:first-child { margin-top: 1.4rem; }

.svc__thumb {
  grid-area: thumb;
  width: 100%;
  aspect-ratio: 2.4;
  object-fit: cover;
  object-position: 50% 45%;
  border: 1px solid rgba(11,13,15,.2);
}
@media (min-width: 760px) {
  /* at 150px the thumbnails were too small to tell a forklift from a loader,
     so they scale with the page instead of sitting at one small fixed width.
     The frame is landscape rather than nearly square: a 1.3 thumbnail stood
     222px tall against two lines of description and left most of a phone
     screen of empty beige under the text on every one of the seven rows. */
  .svc__rows--thumbs .svc__row {
    grid-template-columns: auto clamp(210px, 33vw, 480px) minmax(0, 1fr);
    grid-template-areas: "num thumb copy";
  }
  /* and 1.9 was a letterbox nothing else on the site uses. The home page cards
     carry these same six services at 1.6, so the thumbnails are the same shape
     as the cards rather than a flatter version of them. */
  .svc__thumb { aspect-ratio: 1.6; }
}
/* the thumbnail sets the height of the row and the copy is a heading and two
   lines, so at 1440px every one of the six rows left about 240px of empty
   cream under its text, and because the rows stack that became one continuous
   empty column running the height of the page beside the photographs. The
   copy centres against the photograph instead. Only once the row actually has
   a photograph column: stacked on a phone the copy sits under the picture and
   is already tight. One rule, every row, including the ones nobody reported. */
@media (min-width: 760px) {
  .svc__rows--thumbs .svc__row { align-items: center; }
}
/* six rows of number, photograph left, two lines of copy right, and the copy
   held to 62ch so it stopped two fifths short of the content edge: at 1440px
   every row was a 70px block of text in a 300px row with the right of the
   screen carrying nothing, six times over. The copy runs the full width of its
   column and the photograph swaps sides row by row, so the eye has to travel
   and the rows stop being the same unit repeated. One rule, every row of the
   list. */
.svc__rows--thumbs .svc__row p { max-width: none; }
@media (min-width: 760px) {
  .svc__rows--thumbs .svc__row:nth-child(even):has(.svc__thumb) {
    grid-template-columns: auto minmax(0, 1fr) clamp(210px, 33vw, 480px);
    grid-template-areas: "num copy thumb";
  }
}
/* not every row has a photograph yet, and holding the thumbnail column open on
   the ones that do not left a 400px hole between the number and the copy on
   desktop and an empty slot in the stack on a phone. A row without a picture
   drops the column instead of reserving it, so the copy runs from the number
   across the full width and the row reads as a different one rather than a
   failed one. One rule, every list, including the rows that get a photograph
   later. */
.svc__rows--thumbs .svc__row:not(:has(.svc__thumb)) {
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "num copy";
}
/* at 390px the photograph sat flush at the container edge while the heading and
   the copy were indented past the number gutter, so every row ran two competing
   left edges the whole way down the page. Below the photo column the row is a
   single column: the number sits above the heading, and the photograph, the
   copy and the dividing rules all start on the same edge. One rule, every row,
   including the ones nobody has reported. */
@media (max-width: 759.98px) {
  .svc__rows--thumbs .svc__row,
  .svc__rows--thumbs .svc__row:not(:has(.svc__thumb)) {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "num" "copy" "thumb";
    gap: .5rem 0;
  }
  .svc__rows--thumbs .svc__num { min-width: 0; }
}
/* forklift-yard.webp has the forklift small and in the middle distance behind
   a ute tray of brake drums, and it is the only forklift photograph there is,
   so the frame is pulled in to the forklift and its mast. The wider box that
   sat here kept the drums, and the drums then filled the foreground and left
   the machine named in the heading as a quarter of its own picture with the
   mast cut off the top. The box moves right and up: the forklift, its mast
   and the wheel it is standing over are the subject and the drums are out of
   frame. Browsers without object-view-box get the untrimmed shot.
   That box still ran out to the right edge of the photograph, which is empty
   carpark, and stopped 44.5% up from the bottom, which is exactly where the
   wheel the caption names is lying. In the 1.33 gallery frame it came out with
   the mast cut off the top and the wheel off the bottom: a picture of a
   forklift with its wheel off showing neither. The box is pulled in to the
   machine and taken down past the wheel, and it is now 1.33 itself, which is
   the gallery frame, so the gallery adds no second crop. */
@supports (object-view-box: inset(0%)) {
  .shot--forklift { object-view-box: inset(0% 16.25% 38% 37.25%); }
}
/* utes-excavator.webp read as another photograph of the two white utes, which
   is what rows 01 and 03 of the services list already are, so the one row in
   six that is genuinely a different machine looked like the same service
   again. The box drops the left ute and the foreground dirt and holds the
   excavator, its boom and the orange body across the frame. The machine is
   behind the utes in the shot, so the right-hand ute cannot be cropped out
   without losing the excavator with it. Browsers without object-view-box get
   the vertical crop below, same as the other uses of this photograph. */
.shot--excavator { object-position: 50% 42%; }
@supports (object-view-box: inset(0%)) {
  .shot--excavator { object-view-box: inset(33.75% 0% 44.45% 38%); }
}

/* ------------------------------------------------------- service cards */
/* the six primary services run as cards rather than as another numbered list:
   the vertical list put the light vehicle work eight rows down a band the
   length of the page, where the two categories the ads are bought against were
   the last things read. Three across and two down puts cars, diesel and mobile
   in the first row at the same size as the heavy gear under it. The card is the
   .feat card made into a link: same photograph with the yellow rule under it,
   same heading, same arrow, so this is the system it already has rather than a
   new one. */
.svccards { display: grid; gap: clamp(1.6rem, 3vw, 2.4rem); }
@media (min-width: 620px) { .svccards { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { .svccards { grid-template-columns: repeat(3, 1fr); } }
.svccard {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  transition: transform .2s ease;
}
.svccard:hover { transform: translateY(-3px); }
.svccard img {
  width: 100%;
  aspect-ratio: 1.6;
  object-fit: cover;
  margin-bottom: 1.2rem;
  border-bottom: 5px solid var(--hi);
}
/* the one card with no photograph takes the rule above the words instead of
   under a picture, exactly the way .feat--noshot does on the workshop band, so
   it reads as a card that was set without one rather than a card whose picture
   failed. It still fills its cell, because a short card in a grid of six is the
   thing that looks broken. */
.svccard--noshot { border-top: 5px solid var(--hi); padding-top: 1.2rem; }
/* in one column there is nothing for the card to line up with and the rule
   above the words is enough. The moment the cards sit side by side it is not:
   a card that starts at the top of its cell floats its heading 223px above the
   headings either side of it, and a row of six comes out as two pictures that
   failed to load rather than two cards set without one. The panel takes the
   exact box the photograph takes, in the petrol the site already gives a band
   of its own, so every heading in the row starts on the same line and the
   variant reads as a decision. One rule, every grid of these, including the
   cards that get a photograph later. */
@media (min-width: 620px) {
  .svccard--noshot { border-top: 0; padding-top: 0; }
  .svccard--noshot::before {
    content: "";
    flex: none;
    width: 100%;
    aspect-ratio: 1.6;
    background: var(--petrol);
    border-bottom: 5px solid var(--hi);
    margin-bottom: 1.2rem;
  }
}
.svccard h3 { margin-bottom: .55rem; }
.svccard p { font-size: .95rem; color: var(--grey-lt); }
.paper .svccard p, .paper-2 .svccard p { color: var(--grey-dk); }
.svccard .arrowlink { margin-top: auto; }
.svccard:hover h3, .svccard:focus-visible h3 {
  text-decoration: underline;
  text-decoration-thickness: 3px;
  text-underline-offset: 4px;
}
.svccard:hover .arrowlink span { transform: translateX(5px); }
/* six cells identical in a three by two grid set the two services the client
   asked to lead at exactly the same weight as the four under them, so the grid
   flattened out at the one moment the page is meant to say what the business is
   for. The lead pair takes a double cell on a row of its own and the other four
   run underneath at the size they were. Nothing is removed and the order is the
   one the client asked for. */
@media (min-width: 1000px) {
  .svccards { grid-template-columns: repeat(12, 1fr); }
  .svccard { grid-column: span 3; }
  .svccard--lead { grid-column: span 6; }
  .svccard--lead img { aspect-ratio: 1.9; }
  .svccard--lead h3 { font-size: clamp(1.7rem, 2.9vw, 2.15rem); }
  .svccard--lead p { font-size: 1.05rem; }
}
/* stacked on a phone the six cards ran 3,177px, close to four screens of the
   same unit repeating through the middle of the page: photograph, yellow rule,
   heading, three lines, explore service, six times. The lead pair keeps its
   photograph and the four under them drop theirs and run as a compact list, so
   every service stays on the page and about a thousand pixels of it does not.
   One rule, every grid of these. */
@media (max-width: 619.98px) {
  .svccards { gap: 1.6rem; }
  .svccard:not(.svccard--lead) img { display: none; }
  .svccard:not(.svccard--lead) {
    border-top: 4px solid var(--hi);
    padding-top: 1rem;
  }
  .svccard:not(.svccard--lead) .arrowlink { border-bottom: 0; padding-bottom: 0; }
}

/* --------------------------------------------- specialist capabilities */
/* engine rebuilds, hydraulics, fabrication, diagnostics and fleet work were
   five of the eight primary services, which is how the homepage ended up
   reading as an equipment catalogue. They are capabilities that sit inside the
   six services above, so they run under them at list size rather than at card
   size, on the same ruled grid the equipment index uses. */
.caps {
  display: grid;
  gap: 0;
  border-top: 2px solid rgba(255,255,255,.6);
}
.paper .caps, .paper-2 .caps { border-top-color: rgba(11,13,15,.8); }
@media (min-width: 700px) { .caps { grid-template-columns: 1fr 1fr; gap: 0 2.5rem; } }
@media (min-width: 1060px) { .caps { grid-template-columns: repeat(3, 1fr); } }
.caps > * {
  padding: 1.15rem 0;
  border-bottom: 1px solid rgba(255,255,255,.15);
  text-decoration: none;
  color: inherit;
  display: block;
}
.paper .caps > *, .paper-2 .caps > * { border-bottom-color: rgba(11,13,15,.15); }
.caps h3 {
  font-family: var(--fd);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(1.2rem, 2vw, 1.45rem);
  line-height: 1;
  margin-bottom: .45rem;
  display: flex;
  align-items: baseline;
  gap: .6rem;
}
.caps h3::before {
  content: "";
  width: 9px; height: 9px;
  background: var(--hi);
  flex: none;
}
/* .92rem was 14.7px, the smallest body copy on the site, on the one block that
   sells the engine, hydraulic, fabrication, diagnostic and fleet work. It runs
   at 16px, the size of the copy in the numbered rows directly above it. */
.caps p { margin: 0; font-size: 1rem; color: var(--grey-lt); max-width: 44ch; }
.paper .caps p, .paper-2 .caps p { color: var(--grey-dk); }
.caps a:hover h3 { color: var(--hi-lt); }
.paper .caps a:hover h3, .paper-2 .caps a:hover h3 { color: var(--grey-dk); }
/* four of these tiles open a page and two are plain text, and the only thing
   separating them was a hover colour, so on a phone all six read the same and
   four of them were links nobody could see. The ones that go somewhere carry
   an arrow at the end of the heading. */
.caps a[href] h3::after {
  content: "\2192";
  margin-left: auto;
  padding-left: .6rem;
  color: var(--hi-lt);
}
.paper .caps a[href] h3::after, .paper-2 .caps a[href] h3::after { color: var(--ink); }

/* ----------------------------------------------------------- work photos */
/* the photographs on a service page were one background wash behind the
   heading and nothing after it, so every page below the hero was a column of
   text. This band is the proof: big enough to see the actual job, captioned
   with what is genuinely in the frame and nothing that was not supplied. */
.works { display: grid; gap: clamp(1rem, 2vw, 1.5rem); }
@media (min-width: 700px) { .works { grid-template-columns: repeat(3, 1fr); } }
.works figure { margin: 0; }
.works img {
  width: 100%;
  aspect-ratio: 1.33;
  object-fit: cover;
}
/* one large image with the rest beside it, for the pages where the subject
   only has a single photograph worth running big */
@media (min-width: 700px) {
  .works--feature > :first-child { grid-column: span 2; grid-row: span 2; }
  .works--feature > :first-child img { aspect-ratio: 1.55; }
}
/* two and one are the counts the library can currently fill on some pages, and
   a pair or a single sitting in a three column grid leaves empty cells beside
   it that read as pictures that failed to load */
@media (min-width: 700px) {
  .works--pair { grid-template-columns: 1fr 1fr; }
  .works--pair img { aspect-ratio: 1.5; }
}
.works--solo { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 700px) { .works--solo { grid-template-columns: minmax(0, 1fr); } }
.works--solo img { aspect-ratio: 2.1; }
/* the Out on site group on our work runs to five, and an odd count in the two
   column pair leaves the last one with an empty cell beside it, which is the
   same hole the rule above exists to close. The odd one out takes the whole
   row instead, at the proportions a single already uses here. One figure, and
   nothing happens to a group with an even count. */
@media (min-width: 700px) {
  .works__wide { grid-column: 1 / -1; }
  .works__wide img { aspect-ratio: 2.1; }
}

/* ----------------------------------------------------------------- split */
.split {
  display: grid;
  gap: clamp(2rem, 4vw, 3.6rem);
  align-items: center;
}
@media (min-width: 900px) {
  .split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .split--wide-l { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); }
  .split--wide-r { grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); }
  .split--flip > :first-child { order: 2; }
}
/* where the second column is a long list, centring the heading against it
   leaves most of a screen empty beside the top of the list */
.split--top { align-items: start; }
/* and starting it at the top only moves the hole down: a heading, a line and a
   button beside a column that runs hundreds of pixels further leaves the rest
   of that half of the band empty. Pinning it made it travel, which did not
   stop it parking the whole gap under itself at the end of the scroll: 350px
   of empty black under the phone button on the home FAQ, 250px on breakdowns.
   The short column centres against the long one instead, so the space is split
   above and below it rather than always dropped in the same place. It goes on
   the column rather than on .split, because the same band is used elsewhere
   with two columns of similar length. One rule, all eleven of these, including
   the enquiry blocks on the eight service pages that nobody has reported. */
@media (min-width: 900px) {
  .stick-col { align-self: center; }
}
.split img { width: 100%; object-fit: cover; }
/* a 490x308 photograph centred in a 700px band left about 270px of flat teal
   under it and 120px over it, so the picture sat adrift in the emptiest band
   on the home page. The column stretches and the shot takes the height the
   bullets opposite set, rather than floating in the middle of it. */
@media (min-width: 900px) {
  .split__fill { align-self: stretch; }
  .split__fill img { height: 100%; aspect-ratio: auto; }
}
/* that works for a photograph the frame can take a bite out of. utes-workshop
   is a 1500x929 landscape and the stretched column made it a 494x666 portrait
   box, so cover sliced 27% off each side, cut the left ute vertically through
   its cab and left a sliver of its yellow stripe jammed against the frame edge.
   A landscape shot keeps its own proportion and centres in the column instead
   of filling it, so both utes stay in shot. */
@media (min-width: 900px) {
  .split__fill--wide { display: flex; flex-direction: column; justify-content: center; }
  .split__fill--wide img { height: auto; aspect-ratio: 1.6; }
}
/* the downtime shot was asked to start level with the eyebrow beside it rather
   than float in the middle of the column, and to run as tall as the text block.
   It starts at the top. It does not run the full height: utes-workshop is a
   1500x929 landscape and the two utes fill about 70% of its width, so a 4:5
   frame would take the rear of the left tray and the front of the right bull
   bar off with it. 1.2 is as tall as this photograph goes with both utes still
   whole, and the window is held a little left of centre, which is where the
   pair sits in the shot. */
@media (min-width: 900px) {
  .split__fill--top { justify-content: flex-start; }
  .split__fill--top img { height: auto; aspect-ratio: 1.2; object-position: 36% 50%; }
}

.shot-tall { aspect-ratio: .78; }
.shot-wide { aspect-ratio: 1.6; }
.shot-sq { aspect-ratio: 1.15; }
/* utes-excavator.webp is a 1000x1778 portrait with the two utes and the
   excavator in a band across the middle of it, roughly 39% to 54% down. Every
   frame it lands in has to be held on that band, or the crop comes out as sky
   and gum trees above it or bare dirt below it. One rule, every use of it.
   At 44% the bottom third of the frame was coming out as bare dirt, so the
   crop is pulled up and the dirt is trimmed from the bottom rather than the
   trees from the top. It stops at 40%: at 30% the window bottom lands above
   the wheels and the 1.9 and 2.4 thumbnails cut the utes off at the axles. */
.shot--utes { object-position: 50% 40%; }
/* and a 1000x1778 portrait does not belong in a .78 frame at any width: it
   came out as sky over a thin band of machinery over bare dirt. Wherever this
   shot is asked for a tall box it gets a landscape one instead, the same
   proportion the services index thumbnails use. */
.shot--utes.shot-tall { aspect-ratio: 1.3; }
/* utes-hilux-yard.webp is 1800x1033 and the top third of it is empty sky, so
   in the 2.4 thumbnail the services index runs below 760px a centred window
   cut both utes off at the bonnet. The window is held on the bottom of the
   frame, which is where the trays, the doors and the wheels are. Above 760px
   the thumbnail is 1.6 and the shot is wider than its box, so nothing is
   trimmed off the top or the bottom there and this does nothing. */
.shot--utes-yard { object-position: 50% 96%; }
/* mobile-fleet-utes-01.webp is 1600x1000 and the four utes sit in a band from
   about 50% to 83% down, with pine trees and sky above them. In the 2.4
   thumbnail the services index runs below 760px a centred window clipped the
   tyres along the bottom edge, so the window is pulled down onto the band.
   Above 760px the thumbnail is 1.6, the same shape as the shot, so nothing is
   trimmed there and this does nothing. */
.shot--fleet-utes { object-position: 50% 58%; }
/* car-vw-repair-01.webp is 1100x688 of a job photographed at an angle: the
   canopy sign runs across the top of the frame and the red car across the
   bottom, so the 2.4 thumbnail cannot hold both. A centred window left the car
   as a strip along the bottom edge, so it sits just below centre: the sign
   loses the top of its lettering rather than the car losing its bonnet. Like
   the rule above, this only applies under 760px. */
.shot--vw-repair { object-position: 50% 54%; }
/* the 4WD on the hoist is a 1126x2000 portrait, and at full column width it
   would run twice as tall as the box it sits in, so it keeps the 900x910 box
   the shot on about has always had and is cropped into it instead. The vehicle,
   the hoist arms and the hub with the wheel off run from about 27% to 68% down
   the frame, so the window is held above centre: that keeps the brake disc in
   and trims the empty concrete at the bottom rather than the roof at the top. */
.shot--4wd { aspect-ratio: 900 / 910; object-position: 50% 44%; }
/* the picture on about is the one the client asked to have rounded corners on,
   so the radius sits on this modifier rather than on .framed, which every other
   photograph on the site shares. */
.shot-round { border-radius: 12px; }
.framed { border: 1px solid rgba(255,255,255,.18); }
.paper .framed, .paper-2 .framed { border-color: rgba(11,13,15,.2); }

.ticks { display: grid; gap: .1rem; margin: 1.6rem 0 2rem; }
.ticks li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .85rem;
  padding: .72rem 0;
  border-top: 1px solid rgba(255,255,255,.17);
  font-weight: 500;
}
.paper .ticks li, .paper-2 .ticks li, .hi .ticks li { border-top-color: rgba(11,13,15,.18); }
.ticks li::before {
  content: "";
  width: 10px; height: 10px;
  background: var(--hi);
  transform: translateY(.55rem);
}
.hi .ticks li::before { background: var(--ink); }
.ticks strong { font-weight: 700; }
.ticks span { display: block; font-size: .93rem; color: var(--grey-lt); }
.paper .ticks span, .paper-2 .ticks span, .hi .ticks span { color: var(--grey-dk); }
/* on the teal bands this list sits beside a .lede paragraph, so at .93rem in
   grey-blue the column carrying the actual detail was both the smaller and the
   dimmer of the two. On petrol it runs at the paragraph's size and close to
   white, which the ground is dark enough to carry. */
.petrol .ticks span { font-size: 1.05rem; color: #e6e6e6; }

/* -------------------------------------------------------------- features */
.feat2 { display: grid; gap: clamp(2rem, 4vw, 3rem); }
@media (min-width: 820px) { .feat2 { grid-template-columns: 1fr 1fr; } }
.feat {
  display: flex;
  flex-direction: column;
}
.feat img {
  width: 100%;
  aspect-ratio: 1.42;
  object-fit: cover;
  margin-bottom: 1.4rem;
  border-bottom: 5px solid var(--hi);
}
/* there is no photograph of Shanno's own fabrication work yet, and an empty
   box beside a real photograph reads as a failed image rather than a held
   space. The card runs as a deliberate text-only one and takes the yellow
   rule the photograph carries on the card next to it. */
.feat--noshot {
  border-top: 5px solid var(--hi);
  padding-top: 1.4rem;
  /* and it stops stretching to the height of the card with the photograph in
     it, or the gap the photograph used to fill just moves under the text */
  align-self: start;
}
/* hung from the top it left most of a screen of empty near-black under the
   link, which reads as a photograph that failed rather than a card that never
   had one. Once the cells sit side by side it centres against the card with
   the picture in it, so the pair balances and the band ends on content. */
@media (min-width: 820px) {
  .feat--noshot { align-self: center; }
}
.feat h3 { margin-bottom: .7rem; }
.feat p { color: var(--grey-lt); }
.paper .feat p, .paper-2 .feat p { color: var(--grey-dk); }
.feat .arrowlink { margin-top: auto; }

.arrowlink {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  font-weight: 700;
  font-size: .87rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  text-decoration: none;
  border-bottom: 2px solid var(--hi);
  padding: .55rem 0 .45rem;
  align-self: flex-start;
}
.arrowlink:hover { color: var(--hi-lt); }
.paper .arrowlink:hover, .paper-2 .arrowlink:hover, .hi .arrowlink:hover { color: var(--grey-dk); }
.arrowlink span { transition: transform .2s ease; }
.arrowlink:hover span { transform: translateX(5px); }

/* ----------------------------------------------------------------- stats */
.stats {
  display: grid;
  gap: 0;
  border-top: 2px solid rgba(11,13,15,.8);
}
@media (min-width: 700px) { .stats { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1050px) { .stats { grid-template-columns: repeat(4, 1fr); } }
.stat {
  padding: 1.9rem 1.6rem 1.9rem 0;
  border-bottom: 1px solid rgba(11,13,15,.18);
}
@media (min-width: 700px) {
  .stat { border-right: 1px solid rgba(11,13,15,.18); padding-left: 1.6rem; }
  .stat:first-child { padding-left: 0; }
  .stat:nth-child(2n) { border-right: 0; }
}
@media (min-width: 1050px) {
  .stat { border-right: 1px solid rgba(11,13,15,.18); }
  .stat:nth-child(2n) { border-right: 1px solid rgba(11,13,15,.18); }
  .stat:last-child { border-right: 0; }
}
.stat b {
  display: block;
  font-family: var(--fd);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(2.1rem, 3.4vw, 2.8rem);
  line-height: .92;
  margin-bottom: .55rem;
}
/* three of the four headings wrap to two lines and one does not, so once the
   row has columns the short one floated its paragraph about 40px above the
   other three. Every heading holds two lines' worth of box, so every paragraph
   starts on the same line. */
@media (min-width: 700px) {
  .stat b { min-height: 1.84em; }
}
.stat p { margin: 0; font-size: .95rem; color: var(--grey-dk); }

/* -------------------------------------------------------------- faq grid */
/* the four questions ran in a single column beside a heading, one line and a
   phone button, so the short column stopped at 419px against 789px of
   questions and left roughly 440 by 700px of empty ground across the lower
   left of the band. The intro sits above the questions at the measure the rest
   of the site sets its section intros at, and the questions run two up
   underneath it, so the band has content the whole way across. */
.faqgrid { display: grid; gap: 1.9rem clamp(2rem, 4vw, 3.5rem); }
@media (min-width: 760px) { .faqgrid { grid-template-columns: 1fr 1fr; } }
.faqgrid h3 { margin-bottom: .55rem; }
.faqgrid p { margin: 0; max-width: 62ch; }

/* ------------------------------------------------------------ quote band */
.quotes { display: grid; gap: 0; }
@media (min-width: 900px) { .quotes { grid-template-columns: repeat(3, 1fr); } }
.quote {
  padding: 2rem 0;
  border-top: 2px solid var(--ink);
  display: flex;
  flex-direction: column;
}
.quote cite { margin-top: auto; }
@media (min-width: 900px) {
  .quote { padding: 0 2rem 0 0; border-top: 0; border-left: 2px solid var(--ink); padding-left: 2rem; }
  .quote:first-child { border-left: 0; padding-left: 0; }
}
.quote blockquote {
  margin: 0 0 1.1rem;
  font-size: 1.02rem;
  line-height: 1.55;
}
.quote blockquote::before {
  content: "“";
  font-family: var(--fd);
  font-weight: 800;
  font-size: 3.4rem;
  line-height: .6;
  display: block;
  margin-bottom: .45rem;
}
.quote cite {
  font-style: normal;
  font-weight: 700;
  font-size: .82rem;
  letter-spacing: .13em;
  text-transform: uppercase;
}

/* home and about were running the same three quotes in the same three columns,
   so the two pages read as one page twice. About runs a single quote large
   until there are real reviews to split between them. */
.quotes--single { display: block; }
.quotes--single .quote { border: 0; padding: 0; }
/* the size only comes in once there is a column to fill: on a phone the band
   is one column wide either way, and setting a quote at 2.4rem there only
   turns four readable lines into five short ones */
@media (min-width: 760px) {
  .quotes--single blockquote {
    font-size: clamp(1.5rem, 2.6vw, 2.4rem);
    line-height: 1.32;
    max-width: 16em;
  }
  .quotes--single blockquote::before { font-size: 4.6rem; }
}

/* the six Google reviews the client supplied are two rows of this band rather
   than the one row it was written for, so the column rule comes off the first
   quote in each row instead of off the first quote only, and the rows take a
   gap a single row never needed. On the charcoal ground the rule is white at
   the weight the capability index uses, because ink on charcoal is not there.
   The stars are the ornament in this variant, so the big opening quote mark
   that does that job in the single row version stands down. */
.quotes--six { row-gap: clamp(2rem, 3.5vw, 2.8rem); }
.quotes--six blockquote::before { display: none; }
.petrol .quote { border-color: rgba(255,255,255,.55); }
@media (min-width: 900px) {
  .quotes--six .quote:first-child { border-left-width: 2px; border-left-style: solid; padding-left: 2rem; }
  .quotes--six .quote:nth-child(3n+1) { border-left-width: 0; padding-left: 0; }
}
.stars { color: var(--hi-lt); letter-spacing: .12em; margin: 0 0 .9rem; }
.paper .stars, .paper-2 .stars { color: var(--hi); }
/* the rating sits under the heading as one line, and wraps to two on a phone
   rather than pushing the stars off the measure */
.rating { display: flex; align-items: center; gap: .65rem; flex-wrap: wrap; font-weight: 700; margin: .95rem 0 0; }
.rating .stars { margin: 0; }
.quote__src { font-size: .72rem; letter-spacing: .13em; text-transform: uppercase; color: var(--grey-lt); margin-top: .35rem; }
.paper .quote__src, .paper-2 .quote__src { color: var(--grey-dk); }

/* placeholder reviews: marked on the page until the client signs off real ones */
.pending {
  display: inline-block;
  margin: 0 0 1.6rem;
  padding: .6rem 1rem;
  border: 2px solid var(--ink);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
}

/* ------------------------------------------------------------------ form */
.formband { display: grid; gap: clamp(2.2rem, 4vw, 3.5rem); }
@media (min-width: 980px) { .formband { grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr); } }
/* on the enquiry bands the left column is a heading, a paragraph and the phone
   number, and the form panel beside it is half as tall again, so centring the
   short column dropped the section's own eyebrow 185px below the top of the
   form it is introducing: the two halves of the band started on different
   lines and the page read as two pages side by side. In a two column band the
   columns start together. One rule, the enquiry block on breakdowns and on all
   eight service pages. */
@media (min-width: 980px) {
  .formband .stick-col { align-self: start; }
}

.contact-card { border-top: 2px solid var(--hi); padding-top: 1.6rem; }
.contact-card dl { margin: 0; display: grid; gap: 1.3rem; }
.paper .contact-card dt, .paper-2 .contact-card dt, .hi .contact-card dt { color: var(--grey-dk); }
.contact-card dt {
  font-size: .73rem;
  font-weight: 700;
  letter-spacing: .17em;
  text-transform: uppercase;
  color: var(--grey-lt);
  margin-bottom: .25rem;
}
.contact-card dd { margin: 0; font-size: 1.05rem; font-weight: 500; }
.contact-card a { text-decoration-color: var(--hi); text-underline-offset: 3px; }
/* the phone and email lines in the contact card rendered at 102 by 19px and the
   ring-us link under the Send button at 80 by 15px, and that second one sits
   exactly where somebody gives up on the form and decides to ring instead. Both
   are the highest intent links on the page and both were a thin line of body
   text. They take a real tap target. One rule, every contact card and all
   eleven forms. */
.contact-card a, .form__note a, .equip__foot a {
  display: inline-block;
  min-height: 44px;
  padding-block: .55rem;
}

.form {
  background: var(--steel);
  color: var(--white);
  padding: clamp(1.4rem, 3vw, 2.2rem);
  border-top: 5px solid var(--hi);
}
.form .muted { color: var(--grey-lt); }
.form__grid { display: grid; gap: 1rem; }
@media (min-width: 560px) { .form__2 { grid-template-columns: 1fr 1fr; } }
.field { display: grid; gap: .4rem; }
.field label {
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--grey-lt);
}
.field input, .field select, .field textarea {
  font-family: var(--fb);
  font-size: 1rem;
  padding: .85rem .9rem;
  min-height: 52px;
  background: var(--ink);
  /* the fields sit on a dark panel, so the border has to be the mid grey or
     the five boxes read as one solid block */
  border: 1px solid var(--grey-dk);
  color: var(--white);
  border-radius: 0;
  width: 100%;
}
/* a textarea does not inherit the body leading, so the one two-line
   placeholder in the form, which is also the only instruction in it, set
   tighter than every other piece of copy on the page and the two lines nearly
   touched at 390px. One rule, the textarea on all eleven forms. */
.field textarea { min-height: 118px; resize: vertical; line-height: 1.5; }
.field input::placeholder, .field textarea::placeholder { color: #7b868b; }
.field input:focus, .field select:focus, .field textarea:focus {
  /* the fields sit on the steel panel, where the brand blue is too close to the
     ground to read as a focus ring, so it takes the bright blue */
  outline: 3px solid var(--hi-lt);
  outline-offset: 0;
  border-color: var(--hi-lt);
}
.form .btn { width: 100%; margin-top: .3rem; }
.form__note { font-size: .84rem; color: var(--grey-lt); margin: .9rem 0 0; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* the acknowledgement sits with the Send button at the bottom of the form,
   not at the top of it where a phone never scrolls back to */
.form__msg {
  margin: 0;
  padding: .85rem 1rem;
  font-weight: 600;
  font-size: .95rem;
  display: none;
}
.form__msg.is-on { display: block; }
.form__msg--bad { background: #5a1b12; color: #ffd9d2; }
.form__msg--ok { background: var(--hi); color: var(--white); }

/* ------------------------------------------------------------- call band */
.callband {
  display: grid;
  gap: 1.6rem;
  align-items: center;
}
@media (min-width: 880px) {
  .callband { grid-template-columns: minmax(0, 1fr) auto; gap: 2.5rem; }
}
.callband h2 { margin-bottom: .6rem; }
.callband p { margin: 0; max-width: 52ch; }

/* -------------------------------------------------------------- pagehead */
.pagehead { position: relative; background: var(--petrol-dk); color: var(--white); overflow: hidden; }
.pagehead--photo::before {
  content: "";
  position: absolute; inset: 0;
  background-image: var(--bg);
  /* cover unless a page asks for more. A portrait shot in a band that goes
     nearly square on a phone shows its full width and nothing can be pushed
     into frame with position alone, so a page can scale up and then crop. */
  background-size: var(--bgsize, cover);
  background-position: var(--bgpos, center);
  /* at .48 under a flat wash the vehicle was a ghost behind the type on every
     inner page, so four pages read as the same page above the fold and the
     photographs did no work at all. The photograph is lifted and the wash
     falls away faster across it, while the left column where the type sits
     stays as dark as it was. */
  opacity: .6;
}
/* a page can set --bgpos-sm where the centre crop loses its subject once the
   frame goes narrow and tall */
@media (max-width: 620px) {
  .pagehead--photo::before {
    background-size: var(--bgsize-sm, var(--bgsize, cover));
    background-position: var(--bgpos-sm, var(--bgpos, center));
  }
}
/* the about hero is the one page where the photograph is meant to read as a
   photograph of Shanno's rather than as texture behind the heading */
.pagehead--lift.pagehead--photo::before { opacity: .72; }
.pagehead--photo::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(95deg, var(--petrol-dk) 14%, rgba(16,20,23,.82) 46%, rgba(16,20,23,.26) 100%);
}
/* the sideways gradient covers the left column, which is where the text is at
   desktop. On a phone the text is the whole width, so the paragraph was
   landing on the bright parts of the photograph. The overlay turns downward. */
/* the top of the band is where the breadcrumb sits, and it is the smallest
   type on the page, so the wash stays heavy there and lifts underneath it,
   behind the headline, where the photograph has something to show */
@media (max-width: 620px) {
  .pagehead--photo::after {
    background: linear-gradient(180deg, rgba(16,20,23,.9) 0%, rgba(16,20,23,.86) 12%, rgba(16,20,23,.58) 34%, rgba(16,20,23,.8) 100%);
  }
}
.pagehead__in {
  position: relative; z-index: 1;
  padding-block: clamp(2.2rem, 5vw, 4rem) clamp(2.4rem, 5vw, 4rem);
  max-width: 860px;
}
/* the downward wash lifts to .58 about a third of the way into the band,
   which is exactly where the headline and the paragraph sit, and on a busy
   frame that is not enough: the seven line intro on /services/ ran straight
   across the Hino's windscreen and grille, the brightest and most detailed
   part of the photograph. Raising the wash over the whole image would fix the
   type and flatten the picture with it, so the shade sits behind the text
   block instead and fades out above and below it, which leaves the top and
   bottom of the frame as photograph. One rule, every page header with a
   photograph on it, including the ones nobody has reported. */
@media (max-width: 620px) {
  .pagehead--photo .pagehead__in::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(180deg,
      rgba(16,20,23,0) 0%,
      rgba(16,20,23,.5) 12%,
      rgba(16,20,23,.5) 88%,
      rgba(16,20,23,0) 100%);
  }
}
.pagehead h1 { margin-bottom: 1.1rem; }
.pagehead .lede { color: #cfd6d9; max-width: 54ch; margin-bottom: 1.8rem; }

.crumbs {
  display: flex;
  flex-wrap: wrap;
  gap: .45rem;
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: #a2a2a2;
  margin: 0 0 1.4rem;
}
.crumbs a { text-decoration: none; padding: .75rem 0; margin: -.75rem 0; }
.crumbs a:hover { color: var(--hi-lt); }
.crumbs li + li::before { content: "/"; margin-right: .45rem; color: #737373; }
.crumbs li { display: flex; align-items: center; }

/* ------------------------------------------------------------ prose bits */
.prose h2 { margin-top: 2.6rem; margin-bottom: 1rem; }
.prose h2:first-child { margin-top: 0; }
.prose h3 { margin-top: 2rem; margin-bottom: .6rem; }
.prose p { max-width: 68ch; }
.prose a { text-decoration-color: var(--hi); text-decoration-thickness: 2px; text-underline-offset: 3px; }

.checklist {
  display: grid;
  gap: 0;
  margin: 1.5rem 0 0;
  border-top: 2px solid rgba(11,13,15,.8);
}
@media (min-width: 640px) { .checklist { grid-template-columns: 1fr 1fr; gap: 0 2.4rem; } }
.checklist li {
  padding: .78rem 0 .78rem 1.6rem;
  border-bottom: 1px solid rgba(11,13,15,.15);
  position: relative;
  font-weight: 500;
  font-size: .98rem;
}
.checklist li::before {
  content: "";
  position: absolute;
  left: 0; top: 1.28rem;
  width: 9px; height: 9px;
  background: var(--hi);
  border: 1px solid rgba(11,13,15,.4);
}
.dark .checklist, .asphalt .checklist, .petrol .checklist { border-top-color: rgba(255,255,255,.6); }
.dark .checklist li, .asphalt .checklist li, .petrol .checklist li { border-bottom-color: rgba(255,255,255,.15); }
.dark .checklist li::before, .asphalt .checklist li::before, .petrol .checklist li::before { border-color: rgba(255,255,255,.3); }

.tags { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1.4rem; }
.tags li {
  background: var(--hi);
  border: 1px solid rgba(11,13,15,.3);
  padding: .8rem 1.2rem;
  font-family: var(--fd);
  font-weight: 800;
  font-size: clamp(1.15rem, 2vw, 1.45rem);
  line-height: 1;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--ink);
  flex: 1 1 auto;
}
.dark .tags li, .petrol .tags li, .asphalt .tags li { border-color: rgba(255,255,255,.28); }

.linkgrid {
  display: grid;
  gap: 0;
  border-top: 2px solid rgba(11,13,15,.8);
}
@media (min-width: 700px) { .linkgrid { grid-template-columns: 1fr 1fr; gap: 0 2.5rem; } }
.linkgrid a {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 1rem 0;
  border-bottom: 1px solid rgba(11,13,15,.15);
  text-decoration: none;
  font-weight: 600;
}
.linkgrid a:hover { color: var(--grey-dk); }
.linkgrid a span { color: var(--ink); font-size: 1.2rem; }
.dark .linkgrid a span, .petrol .linkgrid a span, .asphalt .linkgrid a span { color: var(--hi-lt); }
.dark .linkgrid, .petrol .linkgrid { border-top-color: rgba(255,255,255,.6); }
.dark .linkgrid a, .petrol .linkgrid a { border-bottom-color: rgba(255,255,255,.15); }

/* ---------------------------------------------------------------- footer */
.footer { background: var(--ink); color: #b9c0c4; font-size: .95rem; }
.footer .muted { color: var(--grey-lt); }
.footer__grid {
  display: grid;
  gap: 2.2rem;
  padding-block: clamp(2.6rem, 5vw, 4rem) 2rem;
}
@media (min-width: 720px) { .footer__grid { grid-template-columns: 1.3fr 1fr 1fr; } }
@media (min-width: 1020px) { .footer__grid { grid-template-columns: 1.4fr 1fr 1fr 1.1fr; } }
.footer h2 {
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .17em;
  text-transform: uppercase;
  color: var(--hi-lt);
  margin-bottom: 1.1rem;
}
.footer ul { display: grid; gap: .1rem; }
.footer li { line-height: 1.45; }
.footer ul a { display: inline-block; padding-block: .5rem; }
/* plain items sit in the same lists as links, so they need the same spacing
   the anchors get from their padding, and a quieter colour so the service
   areas and the street address do not read as things you can click */
.footer ul li:not(:has(> a)) { padding-block: .5rem; color: #8e979b; }
/* that quieter colour is for one plain item sitting inside a list of links,
   like the street address under the phone number. A whole column of them takes
   it too, and with the Services links on one side and the Get in touch links
   on the other the middle of the three columns came out looking switched off.
   The areas column keeps the spacing and goes back to the footer's own colour. */
.footer .footer__areas li:not(:has(> a)) { color: inherit; }
.footer a { text-decoration: none; }
.footer a:hover { color: var(--hi-lt); text-decoration: underline; }
.footer img.flogo { width: 172px; margin-bottom: 1.1rem; }
.footer__tel {
  font-family: var(--fd);
  font-weight: 800;
  font-size: 1.9rem;
  color: var(--white);
  text-decoration: none;
  display: inline-block;
  line-height: 1;
  padding-block: .5rem;
  margin-bottom: .2rem;
}
.footer__tel:hover { color: var(--hi-lt); }
.footer__legal {
  border-top: 1px solid var(--line-dk);
  padding-block: 1.3rem 2rem;
  display: flex;
  flex-wrap: wrap;
  gap: .6rem 1.4rem;
  justify-content: space-between;
  font-size: .8rem;
  color: #7e878b;
}

/* --------------------------------------------------------------- reveals */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .6s cubic-bezier(.22,.61,.36,1), transform .6s cubic-bezier(.22,.61,.36,1);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { opacity: 1; transform: none; transition: none; }
}
