:root {
  --brand-red: #e8232a;
  --brand-navy: #0b1f3a;
  --ink: #1a2233;
  --muted: #5b6472;
  --line: #e6e8ec;
  --bg-alt: #f6f7f9;
  --radius: 12px;
  --shadow: 0 4px 20px rgba(11, 31, 58, 0.08);
  font-size: 16px;
}

* { box-sizing: border-box; }

html {
  max-width: 100vw;
  overflow-x: hidden;
}

body {
  margin: 0;
  max-width: 100vw;
  overflow-x: hidden;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--ink);
  background: #fff;
  line-height: 1.55;
  /* max-width:100vw (not just overflow-x:hidden) matters here: overflow-x:hidden alone hides the
     resulting scrollbar but doesn't cap body's own width, so a descendant's oversizing anywhere on
     the page still inflates the width every other element calculates "available space" against —
     e.g. a flex row with flex-wrap:wrap will happily not wrap if it thinks it has 500px to work
     with, even though only 430px is actually visible. Hard-capping here forces every descendant to
     size against the true viewport width regardless of any other overflow bug in the tree. */
}

img, video { max-width: 100%; display: block; }

.container {
  width: 100%;
  max-width: 1160px;
  margin: 0 auto;
  padding: 0 24px;
}

a { color: inherit; text-decoration: none; }

.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--brand-red);
  margin: 0 0 8px;
}
.eyebrow.center { text-align: center; }
.center { text-align: center; }

h1, h2, h3 { color: var(--brand-navy); line-height: 1.2; margin: 0 0 16px; }
h1 { font-size: clamp(2rem, 4vw, 2.75rem); }
h2 { font-size: clamp(1.5rem, 3vw, 2.1rem); }
h3 { font-size: 1.25rem; margin-bottom: 8px; }

p { color: var(--muted); margin: 0 0 16px; }
.lead { font-size: 1.1rem; }
.lead-narrow { max-width: 620px; margin: 0 auto 32px; }

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 22px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.95rem;
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.btn-lg { padding: 15px 28px; font-size: 1rem; }
.btn-primary { background: var(--brand-red); color: #fff; }
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 8px 18px rgba(232,35,42,0.3); }
.btn-outline { border-color: var(--brand-navy); color: var(--brand-navy); background: #fff; }
.btn-outline:hover { background: var(--brand-navy); color: #fff; }
.btn-ghost { color: var(--brand-navy); font-weight: 600; }
.btn-ghost:hover { color: var(--brand-red); }

/* Header */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255,255,255,0.95);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--line);
}
.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-top: 12px;
  padding-bottom: 12px;
}
/* flex:none + nowrap on the strapline: it's longer than the old "Brick Grab Truck Transport", and
   without these the brand block gets squeezed by the nav and the strapline collides with the first
   menu item (reported 2026-08-31). The nav's own left margin is what keeps them visually separate. */
.brand { display: flex; align-items: center; gap: 10px; flex: none; }
/* The logo is a wide truck+wordmark lockup with a transparent background, not a square mark —
   never crop it to a square. Sizes come from the width/height attributes on the img itself so
   they don't depend on this stylesheet being fresh. */
.brand-logo { height: auto; object-fit: contain; }

/* On the navy footer the logo's black banner would disappear into the background, so it sits on
   a white chip there. The header needs none of this — it's already on white. */
.footer-brand .brand-logo { background: #fff; padding: 6px 10px; border-radius: 8px; }
.brand-text { display: flex; flex-direction: column; line-height: 1.15; }
.brand-text strong { color: var(--brand-navy); font-size: 1.05rem; }
.brand-text small { color: var(--muted); font-size: 0.72rem; white-space: nowrap; }

.main-nav { display: flex; gap: 28px; margin-left: 44px; }
.main-nav a { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; font-size: 0.95rem; color: var(--ink); white-space: nowrap; }
.main-nav a:hover { color: var(--brand-red); }
/* Red icons, matching the shop's iconography (its trust bar and active nav items are all
   --st-red / #e8232a). The label stays ink-coloured — only the icon carries the brand colour,
   same as the shop. */
.nav-ic { flex: none; width: 17px; height: 17px; color: var(--brand-red); }

.header-cta { display: flex; align-items: center; gap: 12px; }
.header-cta .btn-ghost { display: inline-flex; align-items: center; gap: 7px; }
.header-cta .btn-ghost .nav-ic { width: 15px; height: 15px; }

/* "Order Bricks is cut off": the cause is .container's max-width: 1160px, NOT the viewport. The
   header row (brand ~310px + 6 nav items ~770px + phone + two buttons ~480px) needs ~1560px, so it
   overflowed the 1160px container and got clipped by the html/body overflow-x guard — at EVERY
   window width, however wide the screen. Widening the cap for the header alone fixes it; the rest
   of the page keeps the 1160px reading measure. */
.site-header .container { max-width: 1560px; }
.header-cta { flex: none; }
.main-nav { min-width: 0; }

/* Below the widened cap, shed items in priority order rather than clipping: phone number first
   (it's also in the Contact section and the footer), then tighten the nav, then the hamburger
   takes over at 720px. */
@media (max-width: 1500px) {
  .main-nav { gap: 18px; margin-left: 24px; }
  .main-nav a { font-size: 0.9rem; }
}
@media (max-width: 1320px) {
  .header-cta .btn-ghost { display: none; }
}
@media (max-width: 1080px) {
  .main-nav { gap: 12px; margin-left: 14px; }
  .main-nav a { font-size: 0.85rem; }
  .brand-text small { display: none; }
}

.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
}
.nav-toggle span { width: 24px; height: 2px; background: var(--brand-navy); border-radius: 2px; }

/* Hero */
.hero {
  background: linear-gradient(135deg, var(--brand-navy) 0%, #142c52 100%);
  color: #fff;
  padding: 72px 0;
}
.hero-inner {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: 48px;
  align-items: center;
}
.hero .eyebrow { color: #ffb3b3; }
.hero h1 { color: #fff; }
.hero .lead { color: #d7dce6; }
.hero-actions { display: flex; gap: 14px; flex-wrap: wrap; margin: 28px 0 32px; }
.hero-stats { list-style: none; display: flex; gap: 32px; padding: 0; margin: 0; flex-wrap: wrap; }
.hero-stats li { display: flex; flex-direction: column; }
.hero-stats strong { font-size: 1.4rem; color: #fff; }
/* ⚠️ Scoped to .stat-label, NOT a bare "span" — .pay-marks sits inside this same <li> and is built
   from nested <span> elements (.pay-mark, .pay-mark-ecocash, ...). A bare ".hero-stats span" selector
   cascades into every one of those too (descendant selectors don't stop at the first level), silently
   overriding .pay-mark's own color/font-size — the Mastercard/VISA/EcoCash marks rendered as flat grey
   navy-circle blobs, not their own badge, root-caused 2026-09-03. */
.hero-stats .stat-label { font-size: 0.8rem; color: #aab4c6; }

/* Two stacked photos: the brickyard (what we sell) leads at full width, the truck (how it reaches
   you) sits under it, smaller — the visual version of "bricks are the business, transport follows
   the sale". Deliberately not side-by-side: equal-sized photos would read as two equal offerings,
   which is the framing this page moved away from. */
.hero-visual { display: grid; gap: 14px; }
.hero-visual figure { margin: 0; position: relative; border-radius: var(--radius); overflow: hidden; box-shadow: 0 20px 50px rgba(0,0,0,0.35); }
.hero-visual img { width: 100%; display: block; object-fit: cover; }
.hero-photo-main img { aspect-ratio: 16 / 10; }
.hero-photo-sub img { aspect-ratio: 21 / 9; }
.hero-visual figcaption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 18px 14px 8px;
  background: linear-gradient(transparent, rgba(11, 31, 58, 0.85));
  color: #fff;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

/* Sections */
.section { padding: 80px 0; }
.section-alt { background: var(--bg-alt); }

/* About — built to the supplied design 2026-09-03: copy on the left, an icon-led "why builders
   choose us" card grid on the right, then a full-width red stat band and the figure row. */
.about-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: 52px; align-items: start; }
.about-copy p:last-child { margin-bottom: 0; }

/* Centred label with a hairline running out to each edge of its column. */
.choose-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 18px;
  font-size: 1.15rem;
}
.choose-head::before,
.choose-head::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.choose-head em { font-style: normal; color: var(--brand-red); }

/* Six tracks so the row of three (2 each) and the row of two (3 each) share one grid. */
.choose-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 14px; }
.choose-card {
  grid-column: span 2;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px;
  box-shadow: var(--shadow);
}
.choose-card h4 { margin: 0 0 6px; font-size: 1rem; color: var(--brand-navy); }
.choose-card p { margin: 0; font-size: 0.85rem; }
/* The two longer claims sit wider, with the icon beside the text rather than above it. */
.choose-card-wide { grid-column: span 3; display: flex; gap: 14px; align-items: flex-start; }
.choose-card-wide > div { min-width: 0; }

.choose-ic {
  display: grid;
  place-items: center;
  flex: none;
  width: 44px;
  height: 44px;
  margin-bottom: 12px;
  border-radius: 999px;
  background: #fdecec;
  color: var(--brand-red);
}
.choose-card-wide .choose-ic { margin-bottom: 0; }
.choose-ic svg { width: 21px; height: 21px; }
.choose-card .pay-marks { margin-top: 10px; }

/* The red stat band. Full content width — see the markup's own comment. */
.about-band {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin: 48px 0 24px;
  padding: 0;
  background: var(--brand-red);
  border-radius: var(--radius);
  overflow: hidden;
}
.about-band li {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 20px 26px;
  color: #fff;
  border-left: 1px solid rgba(255, 255, 255, 0.24);
}
.about-band li:first-child { border-left: 0; }
.about-band svg { flex: none; width: 26px; height: 26px; }
.about-band div { min-width: 0; }
.about-band strong { display: block; font-size: 1.2rem; line-height: 1.2; }
.about-band span { font-size: 0.8rem; color: rgba(255, 255, 255, 0.88); }

/* ⚠️ A FIXED, shared height rather than a shared aspect-ratio: these two images are different shapes
   (the photo is landscape, the flyer a square poster) and only equal heights make the row read as one
   deliberate band instead of two mismatched blocks. */
.about-figures { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; }
.about-photo,
.about-flyer {
  position: relative;
  height: 400px;
  margin: 0;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
}
.about-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.about-photo figcaption {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 26px;
  /* Three stops, not two: at narrow widths the caption grows taller and the heading rides up into
     the bright part of the photo (the truck's windscreen), where a scrim that only starts at 30%
     leaves white text unreadable. The mid stop guarantees legibility at any caption height while
     still letting the top of the photo show through. */
  background: linear-gradient(180deg, rgba(11, 31, 58, 0) 0%, rgba(11, 31, 58, 0.38) 38%, rgba(11, 31, 58, 0.92) 100%);
}
.about-photo h3 { max-width: 18ch; margin: 0 0 14px; font-size: 1.5rem; color: #fff; }
.about-photo h3::after {
  content: "";
  display: block;
  width: 44px;
  height: 3px;
  margin-top: 12px;
  border-radius: 2px;
  background: var(--brand-red);
}
.about-photo p { max-width: 36ch; margin: 0; font-size: 0.88rem; color: rgba(255, 255, 255, 0.9); }

/* The flyer is artwork with words baked into it — `contain` on its own white ground so nothing is
   cropped, same reasoning as the shop's product cards. */
.about-flyer { display: flex; flex-direction: column; background: #fff; }
.about-flyer img { flex: 1; min-height: 0; width: 100%; object-fit: contain; display: block; }
.flyer-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  background: var(--brand-navy);
  color: #fff;
}
.flyer-bar svg { flex: none; width: 24px; height: 24px; }
.flyer-line { min-width: 0; margin: 0; font-size: 0.86rem; color: #dfe5ee; text-wrap: balance; }
.flyer-bar .btn { flex: none; margin-left: auto; padding: 10px 16px; font-size: 0.85rem; }

.about-close {
  margin-top: 48px;
  padding-top: 32px;
  border-top: 1px solid var(--line);
  text-align: center;
}
.about-close p { max-width: 560px; margin-left: auto; margin-right: auto; }
.about-close-cta { color: var(--brand-navy); font-weight: 700; }

@media (max-width: 720px) {
  .choose-grid { grid-template-columns: minmax(0, 1fr); }
  .choose-card,
  .choose-card-wide { grid-column: auto; }
  .about-band { grid-template-columns: minmax(0, 1fr); }
  .about-band li { border-left: 0; border-top: 1px solid rgba(255, 255, 255, 0.24); }
  .about-band li:first-child { border-top: 0; }
  .about-figures { grid-template-columns: minmax(0, 1fr); }
  .about-photo { height: 280px; }
  .about-flyer { height: auto; }
  .about-flyer img { height: 300px; }
  .flyer-bar { flex-wrap: wrap; }
  .flyer-bar .btn { width: 100%; margin-left: 0; text-align: center; }
}

/* Services — built to the supplied design 2026-09-03. Deliberately NO photographs in the two
   cards: the mockup's truck shots were stock, and stock trucks on a page whose whole argument is
   "these are OUR trucks, not a broker's" undercuts the claim it sits next to. */
.svc-eyebrow {
  display: flex;
  align-items: center;
  gap: 16px;
  max-width: 620px;
  margin-left: auto;
  margin-right: auto;
}
.svc-eyebrow::before,
.svc-eyebrow::after { content: ""; flex: 1; height: 1px; background: #f0c9cb; }

.svc-head { margin-bottom: 28px; }
.svc-head em { font-style: normal; color: var(--brand-red); }
.svc-head::after {
  content: "";
  display: block;
  width: 74px;
  height: 3px;
  margin: 18px auto 0;
  border-radius: 2px;
  background: var(--brand-red);
}

.services-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 28px; margin-top: 40px; }
.service-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 32px;
  box-shadow: var(--shadow);
}
/* The same-day card is the one we want chosen first, so it carries the brand tint; the contract
   card stays neutral white beside it. */
.svc-sameday { background: #fdf6f6; border-color: #f4dcdd; }

.svc-top { display: flex; align-items: center; gap: 16px; margin-bottom: 18px; }
.svc-top h3 { margin: 0 0 2px; }
.svc-ic {
  display: grid;
  place-items: center;
  flex: none;
  width: 52px;
  height: 52px;
  border-radius: 999px;
  background: var(--brand-red);
  color: #fff;
}
.svc-contract .svc-ic { background: var(--brand-navy); }
.svc-ic svg { width: 24px; height: 24px; }
.svc-tag { margin: 0; font-size: 0.85rem; font-weight: 700; color: var(--brand-red); }

/* Bulleted with a filled tick rather than a disc: the tick reads as a promise kept, and it is the
   same shape the shop uses on its own confirmation screens. */
.svc-list {
  list-style: none;
  width: 100%;
  margin: 0 0 24px;
  padding: 18px 0 0;
  border-top: 1px solid var(--line);
  color: var(--muted);
}
.svc-list li { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 9px; font-size: 0.9rem; }
.svc-list .ck { flex: none; width: 18px; height: 18px; margin-top: 1px; color: var(--brand-red); }
.svc-contract .svc-list .ck { color: var(--brand-navy); }
.ck circle { fill: currentColor; }
.ck path { fill: none; stroke: #fff; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.service-card .btn { margin-top: auto; }

/* The four standing promises, as one band under the cards. */
.svc-promises {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 28px;
  margin: 28px 0 0;
  padding: 26px 30px;
  background: #fdf6f6;
  border: 1px solid #f4dcdd;
  border-radius: var(--radius);
}
.svc-promises li { display: flex; align-items: flex-start; gap: 14px; }
.svc-promises svg { flex: none; width: 30px; height: 30px; color: var(--brand-red); }
.svc-promises div { display: flex; flex-direction: column; min-width: 0; }
.svc-promises strong { color: var(--brand-navy); font-size: 0.92rem; }
.svc-promises span { font-size: 0.82rem; color: var(--muted); line-height: 1.45; }

/* Hexham Bricks */
.bricks-section { background: linear-gradient(180deg, #fff 0%, var(--bg-alt) 100%); }
/* Flyer column widened from 0.9fr to 1.25fr (2026-08-31): the flyer is a poster — its selling points
   are text baked into the image, so at the old width they were there but genuinely unreadable. It's
   the one image on the page that has to be *read*, not just seen, so it gets the larger half. */
/* Two explicit rows so the flyer can start level with the tagline instead of the top of the column
   (asked for 2026-09-03): the eyebrow+heading own row 1 of column 1, and the copy and the flyer
   share row 2. Doing it with grid placement rather than a margin on the flyer matters — the heading
   wraps to a different number of lines at almost every width, so a fixed offset would line up at
   exactly one of them and be wrong everywhere else. */
.bricks-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
  grid-template-rows: auto auto;
  column-gap: 48px;
  align-items: start;
}
.bricks-head { grid-column: 1; grid-row: 1; }
.bricks-copy { grid-column: 1; grid-row: 2; }
.bricks-visual { grid-column: 2; grid-row: 2; }
/* Wraps now, because it carries the market comparison as well as the price — same treatment the
   shop's product cards use, so the two properties quote a price the same way. */
.price-card {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 10px;
  background: var(--brand-navy);
  color: #fff;
  border-radius: var(--radius);
  padding: 16px 24px;
  margin: 4px 0 24px;
  position: relative;
}
.price-amount { font-size: 2rem; font-weight: 800; color: #fff; }
.price-unit { font-size: 0.9rem; color: #aab4c6; }
.price-was {
  font-size: 1.15rem;
  color: #aab4c6;
  text-decoration: line-through;
  font-variant-numeric: tabular-nums;
}
.price-off {
  order: -1;
  flex-basis: 100%;
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  color: var(--brand-red);
}
/* Names the struck-through figure as the MARKET rate, not a former Hexham price — a fabricated
   "was" price would be a misleading-pricing claim. $110 is the conservative end of the real
   $105-125 per 1,000 range for ZW red commons (checked 2026-09-02). */
.price-note {
  flex-basis: 100%;
  font-size: 0.78rem;
  color: #aab4c6;
}
.bricks-tagline {
  color: var(--brand-red);
  font-weight: 700;
  font-size: 1.1rem;
  margin: -8px 0 16px;
  padding-bottom: 6px;
  border-bottom: 3px solid var(--brand-red);
  display: inline-block;
}
/* Flyer lightbox. Scrolls rather than shrink-to-fit — the flyer is a poster whose selling points are
   text baked into the image, so it has to be readable at full width, not letterboxed into the
   viewport. Tapping it used to open the raw JPG in a new tab, which had no way back out other than
   closing the tab (reported 2026-09-03).
   ⚠️ The [hidden] rule is not redundant: `display: flex` below would otherwise beat the attribute's
   own `display: none`, and the overlay would sit over the page permanently. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  justify-content: center;
  padding: 56px 16px 40px;
  overflow: auto;
  overscroll-behavior: contain;
  background: rgba(11, 31, 58, 0.88);
  backdrop-filter: blur(3px);
}
.lightbox[hidden] { display: none; }
.lightbox img {
  width: min(900px, 100%);
  height: auto;
  align-self: flex-start;
  border-radius: 10px;
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.5);
}
.lightbox-close {
  position: fixed;
  top: 14px;
  right: 16px;
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
}
.lightbox-close:hover { background: var(--brand-red); }
.lightbox-close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
/* Cursor cue that the flyer opens larger. */
.bricks-flyer-link { cursor: zoom-in; }
.bricks-visual { display: flex; flex-direction: column; gap: 10px; }
.bricks-flyer {
  width: 100%;
  /* `contain`, not `cover`, and no forced square: the source flyer happens to be 1:1 today, but the
     moment a replacement isn't, `cover` would silently crop the very selling points this image
     exists to show. Letterboxing is the safe failure here; cropping text is not. */
  object-fit: contain;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  display: block;
}
/* Tap/click to open the full-resolution flyer — on a phone even the widened column is small for
   reading poster text, so there's always an escape hatch to the real thing. */
.bricks-flyer-link { display: block; }
.bricks-flyer-hint {
  margin: 0;
  text-align: center;
  font-size: 0.8rem;
  color: var(--muted);
}

/* Fleet */
.fleet-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; }
.fleet-photo {
  margin: 0;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
  aspect-ratio: 4 / 3;
}
.fleet-photo img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.25s ease; }
.fleet-photo:hover img { transform: scale(1.05); }


/* Video section */
.video-grid {
  display: grid;
  /* minmax(0, 1fr), not plain 1fr — a <video>'s intrinsic width (848px, the source file's actual
     resolution) otherwise forces its grid track to stay that wide regardless of width:100%, the
     classic CSS Grid "blowout" quirk (grid tracks default to a content-based min-width, not 0). That
     caused page-wide horizontal overflow on mobile, shifting everything below it left. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 24px;
  margin-top: 40px;
  align-items: start;
}
.video-item {
  width: 100%;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  background: #000;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

/* Testimonials */
.testimonial-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
.testimonial-card {
  margin: 0;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px;
  box-shadow: var(--shadow);
}
.testimonial-card.placeholder { border-style: dashed; background: var(--bg-alt); }
.testimonial-card p { color: var(--ink); font-style: italic; }
.testimonial-card cite { font-style: normal; font-size: 0.85rem; color: var(--muted); }

/* Contact — built to the supplied design 2026-09-03. */
.contact-grid { display: grid; grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); gap: 56px; }
.contact-tagline { color: var(--brand-red); font-weight: 700; margin-bottom: 18px; }

/* Signpost for the bricks path, since the copy around it now speaks to transport enquiries. */
.contact-bricks-note {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 24px 0 0;
  padding: 16px 18px;
  background: #fdf6f6;
  border: 1px solid #f4dcdd;
  border-radius: var(--radius);
  font-size: 0.92rem;
}
.contact-bricks-note svg { flex: none; width: 26px; height: 26px; color: var(--brand-red); }
.contact-bricks-note a { font-weight: 700; color: var(--brand-red); text-decoration: none; }
.contact-bricks-note a:hover { text-decoration: underline; }

.contact-us-head {
  margin: 32px 0 0;
  padding-bottom: 12px;
  border-bottom: 2px solid var(--brand-red);
  font-size: 1.05rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Two up, each row an icon beside its label and value(s). Office hours takes the last row on its
   own rather than being forced into a pair. */
.contact-list {
  list-style: none;
  padding: 0;
  margin: 20px 0 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 22px 20px;
}
.contact-list li { display: flex; align-items: flex-start; gap: 12px; }
.contact-list li:last-child { grid-column: 1 / -1; }
.contact-list svg { flex: none; width: 22px; height: 22px; color: var(--brand-red); margin-top: 2px; }
.contact-list li > div { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.contact-label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--brand-red); font-weight: 700; }
.contact-list a,
.contact-list li > div > span { font-size: 0.92rem; color: var(--ink); }
.contact-list a { font-weight: 500; }
.contact-list a:hover { color: var(--brand-red); }
.contact-list em { font-style: italic; color: var(--muted); }

/* ⚠️ Links sitting INSIDE a paragraph need this explicitly. The global `a { color: inherit;
   text-decoration: none }` reset means a bare <a> in prose is indistinguishable from the text around
   it — "shop.hexhamexpress.com" in the note below read as plain text and nobody could tell it was
   clickable (reported 2026-09-03). Same treatment .order-track already uses, so the two match. */
.contact-shop-note a,
.form-note a {
  font-weight: 600;
  color: var(--brand-navy);
  text-decoration: underline;
}
.contact-shop-note a:hover,
.form-note a:hover { color: var(--brand-red); }

.quote-form {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 32px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  box-shadow: var(--shadow);
}
.quote-form label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--brand-navy);
}
.quote-form input,
.quote-form select,
.quote-form textarea {
  font: inherit;
  padding: 11px 14px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: #fff;
  color: var(--ink);
}
.quote-form input:focus,
.quote-form select:focus,
.quote-form textarea:focus {
  outline: 2px solid var(--brand-red);
  outline-offset: 1px;
}
.form-submit { justify-content: center; width: 100%; margin-top: 4px; }
.form-reassure {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  margin: 0;
  font-size: 0.78rem;
  color: var(--muted);
  text-align: center;
}
.form-reassure svg { flex: none; width: 14px; height: 14px; }

.hp-field { position: absolute; left: -9999px; }
.form-status { font-size: 0.85rem; color: var(--muted); margin: 0; min-height: 1.2em; }
.form-status.success { color: #1a7a3a; font-weight: 600; }
.form-status.error { color: var(--brand-red); font-weight: 600; }

/* Footer */
.site-footer { background: var(--brand-navy); color: #cbd3e1; padding: 40px 0; }
.footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 24px;
}
.footer-brand { display: flex; align-items: center; gap: 12px; }
.footer-brand strong { color: #fff; display: block; }
.footer-brand p { margin: 0; font-size: 0.8rem; color: #aab4c6; }
/* flex-wrap + nowrap on each item: without them the five links stay on one squeezed row on a phone
   and every multi-word label breaks mid-name ("Shop / Bricks", "Delivery / Services"). */
.footer-links { display: flex; flex-wrap: wrap; gap: 12px 20px; }
.footer-links a { font-size: 0.85rem; color: #cbd3e1; white-space: nowrap; }
.footer-links a:hover { color: #fff; }

/* Sized for touch (36px), and the row grows on its own as more channels are added. */
.footer-social { display: flex; gap: 10px; }
.soc {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  color: #cbd3e1;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.soc:hover,
.soc:focus-visible {
  background: var(--brand-red);
  border-color: var(--brand-red);
  color: #fff;
}

/* The second intro paragraph sits closer to the first than a standalone .lead-narrow would. */
.services-scale { margin-top: -14px; }
.footer-copy { width: 100%; text-align: center; font-size: 0.78rem; color: #7d879c; margin: 8px 0 0; }

/* Responsive */
@media (max-width: 900px) {
  .hero-inner { grid-template-columns: minmax(0, 1fr); }
  .about-grid { grid-template-columns: minmax(0, 1fr); }
  .bricks-grid { grid-template-columns: minmax(0, 1fr); }
  /* ⚠️ The explicit placements above MUST be reset here, not just the column count. `grid-column: 2`
     on .bricks-visual survives into the one-column layout and makes the browser create an IMPLICIT
     second column for it — the copy gets squeezed into a ~120px ribbon with the flyer overlapping it
     (reported on an iPhone 14 Pro Max, 2026-09-03). */
  .bricks-head,
  .bricks-copy,
  .bricks-visual { grid-column: 1; grid-row: auto; }
  .contact-grid { grid-template-columns: minmax(0, 1fr); }
  .services-grid { grid-template-columns: minmax(0, 1fr); }
  .svc-promises { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .testimonial-grid { grid-template-columns: minmax(0, 1fr); }
  .fleet-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .video-grid { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 720px) {
  .main-nav, .header-cta .btn-ghost { display: none; }
  .nav-toggle { display: flex; }
  /* ⚠️ Must live in THIS block, not the About one further up the file: the 900px block above also
     sets .svc-promises, and at equal specificity the later rule wins regardless of which media
     query is "narrower". Putting it earlier left the band stuck at two columns on a phone. */
  .svc-promises { grid-template-columns: minmax(0, 1fr); padding: 22px; }
  .fleet-grid { grid-template-columns: minmax(0, 1fr); }
  .hero-stats { gap: 20px; }

  .main-nav.open {
    display: flex;
    flex-direction: column;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: #fff;
    border-bottom: 1px solid var(--line);
    padding: 16px 24px;
    gap: 16px;
  }
}

/* ── Storefront entry points (added with the Hexham Bricks online shop) ────── */
/* Navy, not red — matches the shop's own top strip (.st-strip, --st-navy) and the fleet app's navy
   app-bar, so the same brand color opens every property instead of two different accent colors
   fighting for "the" top bar (caught 2026-08-31 comparing the two sites side by side). */
.shop-strip {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 10px 16px;
  background: var(--brand-navy);
  color: #fff;
  font-size: 0.92rem;
  font-weight: 600;
  text-align: center;
}
.shop-strip:hover { background: #142c52; }
.shop-strip-tag {
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(255,255,255,0.2);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
/* Was plain underlined text on navy and read as a footnote rather than the site's main action.
   Now a red pill with the same pulse the splash CTA uses, so the two "buy" moments look related. */
.shop-strip-go {
  padding: 4px 14px;
  border-radius: 999px;
  background: var(--brand-red);
  color: #fff;
  font-weight: 800;
  text-decoration: none;
  animation: strip-go-pulse 2.2s ease-in-out infinite;
}
.shop-strip:hover .shop-strip-go { background: #ff4b52; }
@keyframes strip-go-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(232, 35, 42, 0.65); }
  60% { box-shadow: 0 0 0 10px rgba(232, 35, 42, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .shop-strip-go { animation: none; }
}

/* The header now carries a third action (Order Bricks), so nothing in it may wrap — a wrapped nav
   reads as a broken header, which is the opposite of what a new shop link should signal. */
.main-nav a, .header-cta .btn, .brand-text strong, .brand-text small { white-space: nowrap; }

.btn-light { background: #fff; color: var(--brand-navy); border-color: #fff; }
.btn-light:hover { background: #f1f3f7; }

/* On the navy hero, the plain outline button (white fill) would fight the light CTA next to it. */
.hero .btn-outline { background: transparent; border-color: rgba(255,255,255,0.55); color: #fff; }
.hero .btn-outline:hover { background: #fff; color: var(--brand-navy); }

/* Ordering online is the new primary action, so it's the one that survives a narrowing header:
   the phone button goes first, then Request a Quote (still reachable from the hero and #quote-form). */
@media (max-width: 1400px) {
  .header-cta .btn-ghost { display: none; }
}
@media (max-width: 1000px) {
  .header-cta .btn-outline { display: none; }
}

.order-steps {
  list-style: none;
  margin: 22px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.order-steps li {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.95rem;
  color: var(--muted);
}
/* ⚠️ Scoped to .step-num, NOT a bare "span" — step 2's <li> also holds .pay-marks, itself built from
   nested <span> elements. A bare ".order-steps span" selector matched those too (a descendant
   selector reaches every level, not just direct children), forcing .pay-mark's badges into this
   rule's 24px navy circle instead of their own shape — the EcoCash/VISA/Mastercard marks rendered as
   overlapping navy blobs stacked on top of step 3's text. Root-caused and fixed 2026-09-03; same bug
   independently hit .hero-stats span right above. */
.order-steps .step-num {
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: 999px;
  background: var(--brand-navy);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 24px;
  text-align: center;
}
.order-track { margin: 14px 0 0; font-size: 0.9rem; color: var(--muted); }
.order-track a { font-weight: 600; color: var(--brand-navy); text-decoration: underline; }

/* ── Sale splash ─────────────────────────────────────────────────────────── */
.sale-splash {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(11, 31, 58, 0.72);
  backdrop-filter: blur(3px);
  opacity: 1;
  transition: opacity 0.2s ease;
}
.sale-splash.is-hidden {
  opacity: 0;
  pointer-events: none;
}
.sale-card {
  position: relative;
  width: 100%;
  max-width: 420px;
  padding: 40px 32px 32px;
  border-radius: 20px;
  text-align: center;
  color: #fff;
  background: linear-gradient(160deg, var(--brand-navy) 0%, #142c52 65%, var(--brand-red) 160%);
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.45);
  animation: sale-pop 0.35s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}
@keyframes sale-pop {
  from { transform: scale(0.9); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}
.sale-close {
  position: absolute;
  top: 12px;
  right: 14px;
  border: none;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
}
.sale-close:hover { background: rgba(255, 255, 255, 0.22); }
.sale-eyebrow {
  margin: 0 0 6px;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #ffd9d9;
}
.sale-headline {
  margin: 0 0 10px;
  font-size: 2.5rem;
  line-height: 1.05;
  font-weight: 900;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  /* The global `h1, h2, h3 { color: var(--brand-navy) }` rule (an element selector) beats
     .sale-card's inherited `color: #fff` regardless of nesting — every other line in this card sets
     its own color explicitly for exactly this reason; this one didn't, so it rendered navy text on
     the card's navy-to-red gradient and was nearly invisible (reported 2026-08-31).
     Now a BRIGHT red rather than the brand red: #e8232a on this navy only clears ~3:1, which
     technically passes for large bold text and nothing more. #ff4b52 clears ~4.5:1 and is what
     actually makes it shout, which is the entire point of the card. */
  color: #ff4b52;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.55), 0 0 26px rgba(255, 75, 82, 0.45);
}
.sale-sub {
  margin: 0 auto 20px;
  max-width: 30ch;
  font-size: 0.95rem;
  color: #dfe7f2;
}
.sale-price {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 22px;
  padding: 8px 18px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
}
.sale-amount { font-size: 1.6rem; font-weight: 800; }
.sale-unit { font-size: 0.82rem; color: #dfe7f2; }
.sale-was {
  font-size: 1rem;
  color: #dfe7f2;
  opacity: 0.75;
  text-decoration: line-through;
  font-variant-numeric: tabular-nums;
}
/* White on red rather than red on white — the splash card is already a navy/red gradient, so a
   red badge would disappear into it. */
.sale-off {
  align-self: center;
  padding: 3px 8px;
  border-radius: 5px;
  background: #fff;
  color: var(--brand-red);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.04em;
}
.sale-cta {
  display: block;
  position: relative;
  overflow: hidden;
  padding: 16px 20px;
  border-radius: 999px;
  background: #fff;
  color: var(--brand-navy);
  font-weight: 900;
  font-size: 1.15rem;
  letter-spacing: 0.01em;
  text-decoration: none;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.2);
  transition: transform 0.12s ease;
  /* Two layers of "look at me": a slow pulse on the whole button, and a shine sweeping across it.
     Both are decorative only — the button is fully functional with animation disabled (see the
     reduced-motion block below), which is why neither touches opacity or position of the label. */
  animation: sale-cta-pulse 2s ease-in-out infinite;
}
.sale-cta::after {
  content: "";
  position: absolute;
  top: 0;
  left: -60%;
  width: 45%;
  height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.85), transparent);
  transform: skewX(-18deg);
  animation: sale-cta-shine 2.6s ease-in-out infinite;
}
.sale-cta:hover { transform: translateY(-2px) scale(1.02); }
@keyframes sale-cta-pulse {
  0%, 100% { box-shadow: 0 10px 24px rgba(0, 0, 0, 0.2), 0 0 0 0 rgba(255, 75, 82, 0.55); }
  50% { box-shadow: 0 10px 24px rgba(0, 0, 0, 0.2), 0 0 0 14px rgba(255, 75, 82, 0); }
}
@keyframes sale-cta-shine {
  0% { left: -60%; }
  55%, 100% { left: 130%; }
}
@media (prefers-reduced-motion: reduce) {
  .sale-cta { animation: none; }
  .sale-cta::after { display: none; }
  .sale-card { animation: none; }
}
.sale-dismiss {
  display: block;
  margin: 14px auto 0;
  border: none;
  background: none;
  color: rgba(255, 255, 255, 0.75);
  font-size: 0.85rem;
  text-decoration: underline;
  cursor: pointer;
}

@media (max-width: 480px) {
  .sale-headline { font-size: 2rem; }
  /* Two contact columns are too tight to read on a phone — the office address in particular ends up
     wrapping every two or three words. */
  .contact-list { grid-template-columns: minmax(0, 1fr); gap: 18px; }
}

/* ── Payment marks ─────────────────────────────────────────────────────────────
   Same marks the shop uses, drawn inline rather than shipping trademarked artwork of unverified
   licence. Mastercard's mark genuinely is two overlapping circles, so that one is exact; Visa and
   EcoCash are wordmarks in their own brand colours (EcoCash is blue "Eco" + red "Cash" on white,
   matching the real logo). Swap for official files by replacing the element with an <img>. */
/* ⚠️ flex:none on BOTH the row and each mark. Without it the marks are flex items inside whatever
   row they sit in (the order-steps list, the hero stats), so they shrink under pressure and their
   wordmarks clip — "EcoCash" rendered as "Ec", "VISA" as "VIS", and the Mastercard circles squashed
   to an oval (reported 2026-09-02). A logo that has been squeezed out of proportion looks worse than
   no logo, and these are other companies' marks. */
.pay-marks {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  vertical-align: middle;
  flex: none;
  flex-wrap: wrap;
  row-gap: 4px;
}
.pay-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  height: 22px;
  padding: 0 7px;
  border-radius: 4px;
  font-size: 0.72rem;
  font-weight: 800;
  line-height: 1;
  color: #fff;
  white-space: nowrap;
}
.pay-mark-visa { background: #1a1f71; font-style: italic; letter-spacing: 0.05em; }
.pay-mark-ecocash { background: #fff; padding: 0 6px; }
.pay-mark-ecocash b { color: #1b75bc; font-weight: 800; }
.pay-mark-ecocash i { color: #ed1c24; font-weight: 800; font-style: normal; }
.pay-mark-mc { width: 34px; height: 22px; flex: none; }

/* On the splash card the row is centred under the sub-line. */
.sale-card .pay-marks { margin: -6px 0 16px; justify-content: center; }
/* In the hero stat list it sits under the label. */
.hero-stats .pay-marks { margin-top: 5px; }
/* In the numbered order steps the marks get their OWN line rather than trailing the sentence — at
   the end of a wrapping line they had almost no room left and were the first thing to be squeezed. */
.order-steps .pay-marks { display: flex; margin: 6px 0 0; }
