/* =========================================================================
   J2 Patios & Driveways
   Palette is taken from the materials themselves: fresh tarmac, Indian
   sandstone, and the gold aggregate used in their resin-bound drives.
   ========================================================================= */

:root {
  /* Surfaces — fresh tarmac through to weathered granite */
  --tarmac:    #14161a;
  --slate:     #1a1d23;
  --granite:   #262b33;
  --joint:     #333a44;   /* the mortar joint between blocks */

  /* Ink — Indian sandstone, warm not blue */
  --sandstone: #e9e2d6;
  --mortar:    #98a0ab;
  --mortar-dim:#6d7683;

  /* Accent — resin-bound gold aggregate. Used sparingly, on purpose. */
  --amber:     #e5a038;
  --amber-lo:  #b87b22;
  --amber-hi:  #f0af4d;   /* accent, hover */
  --on-accent: #17130a;   /* text sitting on an accent-filled button */

  /* Channel triplets, so translucent overlays can be re-themed too. */
  --scrim-rgb:  20, 22, 26;    /* over photography */
  --header-rgb: 20, 22, 26;    /* the sticky masthead plate */
  --sheen-rgb:  255, 255, 255; /* grain speckle + ghost-button wash */
  --sheen-a: 0.055;
  --ghost-a: 0.07;

  /* Anything printed over a photograph. The scrims underneath stay dark in
     every theme, so these stay light in every theme. */
  --on-media:        #f1f4f8;
  --on-media-dim:    #cfd4db;
  --on-media-accent: var(--amber);
  --on-media-rgb:    233, 226, 214;

  --ink-soft:   #cfd4db;
  --ink-bright: #dfe3e8;

  /* Nav links before the masthead sticks: they sit on the hero photograph,
     so a light theme has to override them or they vanish into it. */
  --nav-ink:    var(--mortar);
  --nav-ink-hi: var(--sandstone);

  --void:     #0d0e11;             /* plate behind a lightbox image */
  --backdrop: rgba(8, 9, 11, 0.9);

  --measure: 68ch;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --shell: 1240px;

  --fs-hero: clamp(2.5rem, 6.6vw, 5.4rem);
  --fs-h2:   clamp(1.85rem, 3.9vw, 3rem);
  --fs-h3:   clamp(1.05rem, 1.5vw, 1.2rem);
  --fs-lead: clamp(1.02rem, 1.5vw, 1.2rem);
  --fs-body: 1.0425rem;
  --fs-sm:   0.875rem;
  --fs-mono: 0.705rem;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--tarmac);
  color: var(--sandstone);
  font-family: "Archivo", "Segoe UI", system-ui, sans-serif;
  font-size: var(--fs-body);
  line-height: 1.62;
  font-variation-settings: "wdth" 100;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }
a { color: inherit; }

h1, h2, h3, .display {
  font-family: "Archivo", "Arial Narrow", sans-serif;
  font-variation-settings: "wdth" 125, "wght" 750;
  line-height: 1.02;
  letter-spacing: -0.022em;
  margin: 0;
  text-wrap: balance;
}

/* Spec lines, eyebrows, counts — the job-sheet voice */
.mono {
  font-family: "JetBrains Mono", ui-monospace, "Cascadia Mono", monospace;
  font-size: var(--fs-mono);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mortar-dim);
}

.shell {
  width: min(100% - (var(--gutter) * 2), var(--shell));
  margin-inline: auto;
}

.section { padding-block: clamp(4rem, 9vw, 7.5rem); position: relative; }
.section > .shell,
.band > .shell,
.footer > .shell { position: relative; z-index: 2; }

/* Clear the fixed masthead when jumping to an anchor */
:target, section[id] { scroll-margin-top: 88px; }

/* Fine aggregate speckle — tarmac chippings, at the edge of perception */
.grain::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(circle at 1px 1px, rgba(var(--sheen-rgb), var(--sheen-a)) 1px, transparent 0);
  background-size: 4px 4px;
  z-index: 1;
}

/* ---------------------------------------------------------------- signature
   The soldier course: an edging band of blocks laid end-on, the way a drive
   is finished at its kerb. One block is set in amber.
   ------------------------------------------------------------------------ */
.course {
  position: relative;
  height: 13px;
  overflow: hidden;
  background:
    repeating-linear-gradient(90deg,
      var(--granite) 0 44px,
      var(--tarmac)  44px 47px);
  border-block: 1px solid var(--joint);
}
/* One block set in amber. Its position is set per-divider via the
   --course-pos custom property (0 = left edge, 1 = right edge), so the
   amber block walks steadily left to right as you scroll down the page. */
.course::after {
  content: "";
  position: absolute;
  inset-block: 0;
  left: calc((100% - 44px) * var(--course-pos, 0));
  width: 44px;
  background: var(--amber-lo);
}

/* -------------------------------------------------------------------- links */
.btn {
  --btn-bg: var(--amber);
  --btn-fg: var(--on-accent);
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.95rem 1.5rem;
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 1px solid transparent;
  border-radius: 2px;
  font-weight: 650;
  font-size: 0.95rem;
  letter-spacing: 0.005em;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.18s var(--ease), background-color 0.18s var(--ease);
}
.btn:hover { transform: translateY(-2px); background: var(--amber-hi); }
.btn:active { transform: translateY(0); }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--sandstone);
  border-color: var(--joint);
  backdrop-filter: blur(6px);
}
.btn--ghost:hover { background: rgba(var(--sheen-rgb), var(--ghost-a)); }

.btn svg { width: 17px; height: 17px; flex: none; }

:where(a, button, input, textarea, select, [tabindex]):focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 3px;
  border-radius: 1px;
}

/* --------------------------------------------------------- text over media
   The hero, the photo bands and the masthead-before-it-sticks all sit on top
   of photographs. Their scrims stay dark whatever palette is active, so their
   text has to stay light too — it must not follow --sandstone into a light
   theme, or it would turn navy on a near-black photo. */
.hero, .band { color: var(--on-media); }
.hero .head p, .band .head p { color: var(--on-media-dim); }
.hero__eyebrow, .band .head__eyebrow { color: var(--on-media-accent); }
/* The promise pledges sit straight on the photograph — unlike .step and .form
   in the other two bands, which have their own themed plates behind them and
   so keep the themed ink. Scoped to .band because .pledges is reused in the
   About section, where it does sit on a normal surface. */
.band .pledge p { color: var(--on-media-dim); }
.band .pledge h3 { border-top-color: var(--on-media-accent); }
.hero .btn--ghost {
  --btn-fg: var(--on-media);
  border-color: rgba(var(--on-media-rgb), 0.35);
}
.masthead { color: var(--on-media); }

/* ------------------------------------------------------------------ header */
.masthead {
  position: fixed;
  inset: 0 0 auto;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 1rem var(--gutter);
  transition: background-color 0.3s var(--ease), border-color 0.3s var(--ease);
  border-bottom: 1px solid transparent;
}
.masthead.is-stuck {
  background: rgba(var(--header-rgb), 0.92);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--joint);
  color: var(--sandstone);
}

.wordmark {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  text-decoration: none;
  margin-right: auto;
}
.wordmark__mark {
  width: 42px;
  height: 42px;
  flex: none;
  color: currentColor;
  transition: transform 0.5s var(--ease);
}

.wordmark:hover .wordmark__mark { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) {
  .wordmark:hover .wordmark__mark { transform: none; }
}
.wordmark__text { line-height: 1.08; }
.wordmark__name {
  display: block;
  font-variation-settings: "wdth" 112, "wght" 700;
  font-size: 0.98rem;
  letter-spacing: -0.005em;
}
.wordmark__sub {
  display: block;
  font-size: 0.66rem;
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--mortar);
  font-family: "JetBrains Mono", monospace;
}

.nav { display: flex; align-items: center; gap: 1.85rem; }
.nav a {
  font-size: 0.92rem;
  text-decoration: none;
  color: var(--nav-ink);
  transition: color 0.18s var(--ease);
}
.nav a:hover { color: var(--nav-ink-hi); }
/* Once there is a plate behind the masthead — stuck, or the mobile panel
   open — the links are on a themed surface and take the themed ink again. */
.masthead.is-stuck .nav a,
.masthead.is-open .nav a { color: var(--mortar); }
.masthead.is-stuck .nav a:hover,
.masthead.is-open .nav a:hover { color: var(--sandstone); }

.masthead .btn { padding: 0.7rem 1.15rem; font-size: 0.88rem; }

.navtoggle { display: none; }

@media (max-width: 900px) {
  .nav, .masthead > .btn { display: none; }
  .navtoggle {
    display: grid;
    place-items: center;
    width: 42px; height: 42px;
    background: transparent;
    border: 1px solid var(--joint);
    border-radius: 2px;
    color: var(--sandstone);
    cursor: pointer;
  }
  .masthead.is-open .nav {
    display: flex;
    position: absolute;
    inset: 100% 0 auto;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0.5rem var(--gutter) 1.25rem;
    background: rgba(var(--header-rgb), 0.98);
    backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--joint);
  }
  .masthead.is-open .nav a { padding: 0.85rem 0; border-top: 1px solid var(--joint); }
}

/* -------------------------------------------------------------------- hero */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  padding-block: 9rem clamp(3rem, 7vw, 5.5rem);
  isolation: isolate;
}
.hero__media { position: absolute; inset: 0; z-index: -2; overflow: hidden; }
.hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(to top, var(--tarmac) 2%, rgba(var(--scrim-rgb), 0.55) 42%, rgba(var(--scrim-rgb), 0.35) 100%),
    linear-gradient(to right, rgba(var(--scrim-rgb), 0.8) 0%, rgba(var(--scrim-rgb), 0.1) 62%);
}

.hero__eyebrow { color: var(--amber); margin-bottom: 1.6rem; }
.hero h1 {
  font-size: var(--fs-hero);
  max-width: 15ch;
  font-variation-settings: "wdth" 118, "wght" 780;
}
.hero h1 em { font-style: normal; color: var(--amber); }
.hero__lead {
  margin: 1.6rem 0 2.4rem;
  max-width: 46ch;
  font-size: var(--fs-lead);
  color: var(--on-media-dim);
}
.hero__actions { display: flex; flex-wrap: wrap; gap: 0.85rem; }

/* Facts laid along the bottom of the hero like an edging course */
.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  margin-top: clamp(3rem, 6vw, 4.75rem);
  border-top: 1px solid var(--joint);
}
.facts__item { padding: 1.4rem 1.4rem 0 0; }
.facts__value {
  display: block;
  font-variation-settings: "wdth" 120, "wght" 720;
  font-size: 1.32rem;
  letter-spacing: -0.015em;
}
.facts__label { display: block; margin-top: 0.3rem; }

/* ------------------------------------------------------------ section head */
.head { max-width: 60ch; margin-bottom: clamp(2.5rem, 5vw, 3.75rem); }
.head__eyebrow { display: block; color: var(--amber); margin-bottom: 1.1rem; }
.head h2 { font-size: var(--fs-h2); }
.head p { margin: 1.15rem 0 0; color: var(--mortar); max-width: 54ch; }

/* A secondary head inside a section, for the "we also do" block under the
   driveway services. Sized down from .head so it clearly sits below it. */
.head--sub {
  margin-top: clamp(3rem, 6vw, 4.5rem);
  margin-bottom: clamp(1.75rem, 3.5vw, 2.5rem);
}
.head--sub h3 {
  font-size: clamp(1.35rem, 2.6vw, 1.85rem);
  font-variation-settings: "wdth" 112, "wght" 700;
}
.head--sub p { margin: 0.9rem 0 0; color: var(--mortar); max-width: 54ch; font-size: 0.94rem; }

/* ---------------------------------------------------------------- services */
.services {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(255px, 1fr));
  gap: 1px;
  background: var(--joint);
  border: 1px solid var(--joint);
}
.svc {
  position: relative;
  background: var(--slate);
  padding: 0;
  overflow: hidden;
  transition: background-color 0.28s var(--ease);
}
.svc:hover { background: var(--granite); }
.svc__media { position: relative; aspect-ratio: 4 / 3; overflow: hidden; }
.svc__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: saturate(0.86) brightness(0.82);
  transition: transform 0.7s var(--ease), filter 0.45s var(--ease);
}
.svc:hover .svc__media img { transform: scale(1.055); filter: saturate(1) brightness(0.95); }
.svc__body { padding: 1.35rem 1.4rem 1.6rem; }
.svc h3 { font-size: var(--fs-h3); font-variation-settings: "wdth" 112, "wght" 700; }
.svc p { margin: 0.6rem 0 1rem; font-size: 0.94rem; color: var(--mortar); }
.svc__spec {
  display: block;
  padding-top: 0.9rem;
  border-top: 1px solid var(--joint);
  color: var(--mortar-dim);
  font-size: 0.66rem;
}

/* Driveways are the main trade, so the three driveway surfaces get their own
   three-up grid with larger photos and body copy, and the remaining services
   sit below in a tighter four-up row. Both collapse to the same auto-fill
   behaviour on narrow screens, so nothing needs a separate mobile layout. */
/* auto-fit, not auto-fill: with a fixed three and four cards these blocks
   would otherwise leave empty tracks showing the grid's joint colour. */
.services--lead { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.services--lead .svc__media { aspect-ratio: 5 / 4; }
.services--lead .svc__body { padding: 1.6rem 1.6rem 1.85rem; }
.services--lead .svc h3 { font-size: calc(var(--fs-h3) * 1.12); }
.services--lead .svc p { font-size: 1rem; }

.services--also { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.services--also .svc__media { aspect-ratio: 3 / 2; }
.services--also .svc__body { padding: 1.15rem 1.2rem 1.35rem; }
.services--also .svc h3 { font-size: calc(var(--fs-h3) * 0.92); }
.services--also .svc p { font-size: 0.88rem; }

/* -------------------------------------------------------------- photo band */
.band {
  position: relative;
  isolation: isolate;
  padding-block: clamp(5rem, 11vw, 9rem);
  overflow: hidden;
}
.band__media { position: absolute; inset: 0; z-index: -2; }
.band__media img { width: 100%; height: 100%; object-fit: cover; }
.band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(105deg, rgba(var(--scrim-rgb), 0.95) 12%, rgba(var(--scrim-rgb), 0.72) 55%, rgba(var(--scrim-rgb), 0.45) 100%);
}

.pledges {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 2rem 2.5rem;
  margin-top: 3rem;
}
.pledge h3 {
  font-size: 1.06rem;
  font-variation-settings: "wdth" 110, "wght" 700;
  padding-top: 1rem;
  border-top: 2px solid var(--amber-lo);
}
.pledge p { margin: 0.65rem 0 0; color: var(--mortar); font-size: 0.94rem; }

/* One person in the team block, so it runs the full width of the section
   rather than sitting as a stranded card in a three-up grid. */
.pledges--solo { grid-template-columns: 1fr; }
.pledges--solo .pledge h3 { font-size: 1.2rem; }
.pledges--solo .pledge p { font-size: 1rem; max-width: 62ch; }

/* ------------------------------------------------------------------- reel
   The source clips are vertical (shot for Facebook/Reels) — rather than
   crop them wide and lose most of the frame, they run as a horizontal
   strip of vertical cards, the way they were actually filmed.
   ------------------------------------------------------------------------ */
.reel {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  padding-bottom: 0.5rem;
  margin: 0 calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  scrollbar-color: var(--joint) transparent;
}
.reel::-webkit-scrollbar { height: 8px; }
.reel::-webkit-scrollbar-thumb { background: var(--joint); border-radius: 4px; }

.clip {
  position: relative;
  flex: none;
  width: min(72vw, 240px);
  aspect-ratio: 9 / 16;
  scroll-snap-align: start;
  border-radius: 3px;
  overflow: hidden;
  background: var(--granite);
  border: 1px solid var(--joint);
}
.clip video { width: 100%; height: 100%; object-fit: cover; }

.clip__tag {
  position: absolute;
  top: 0.65rem;
  left: 0.65rem;
  padding: 0.3rem 0.55rem;
  background: rgba(var(--scrim-rgb), 0.82);
  color: var(--on-media);
  font-family: "JetBrains Mono", monospace;
  font-size: 0.62rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  border-radius: 2px;
  pointer-events: none;
  z-index: 2;
}
.clip__tag--before { color: var(--amber); }

.clip__play {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.clip__play::after {
  content: "";
  width: 52px; height: 52px;
  border-radius: 50%;
  background: rgba(var(--scrim-rgb), 0.68);
  border: 1px solid rgba(var(--on-media-rgb), 0.35);
  backdrop-filter: blur(3px);
  transition: transform 0.2s var(--ease), background-color 0.2s var(--ease);
}
.clip__play::before {
  content: "";
  position: absolute;
  width: 0; height: 0;
  border-style: solid;
  border-width: 9px 0 9px 15px;
  border-color: transparent transparent transparent var(--sandstone);
  z-index: 1;
  transform: translateX(3px);
}
.clip__play:hover::after { transform: scale(1.08); background: rgba(var(--scrim-rgb), 0.85); }
.clip.is-playing .clip__play { opacity: 0; pointer-events: none; }
.clip.is-playing .clip__play::after { transform: scale(0.85); }

@media (max-width: 640px) {
  .clip__play::after { width: 44px; height: 44px; }
}

/* Scrub bar. Sits above the play button so dragging it never triggers play.
   Idle it is a thin hairline; it thickens on hover, focus and while dragging
   so it stays grabbable on a phone without dominating the poster frame. */
.clip__controls {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 1.4rem 0.6rem 0.55rem;
  background: linear-gradient(to top, rgba(var(--scrim-rgb), 0.75), transparent);
}
.clip__bar {
  position: relative;
  flex: 1;
  height: 16px;                 /* generous hit area; the visible track is drawn by ::before */
  display: flex;
  align-items: center;
  cursor: pointer;
  touch-action: none;           /* so a drag scrubs instead of scrolling the reel */
}
.clip__bar::before {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: 3px;
  border-radius: 2px;
  background: rgba(var(--on-media-rgb), 0.3);
  transition: height 0.15s var(--ease);
}
.clip__fill {
  position: relative;
  height: 3px;
  width: 0;
  border-radius: 2px;
  background: var(--amber);
  transition: height 0.15s var(--ease);
}
.clip__knob {
  position: absolute;
  right: -5.5px;
  top: 50%;
  width: 11px; height: 11px;
  border-radius: 50%;
  background: var(--on-media);
  box-shadow: 0 0 0 1px rgba(var(--scrim-rgb), 0.45);
  transform: translateY(-50%) scale(0);
  transition: transform 0.15s var(--ease);
  pointer-events: none;
}
.clip__bar:hover::before,
.clip__bar:focus-visible::before,
.clip.is-scrubbing .clip__bar::before,
.clip__bar:hover .clip__fill,
.clip__bar:focus-visible .clip__fill,
.clip.is-scrubbing .clip__fill { height: 5px; }

.clip__bar:hover .clip__knob,
.clip__bar:focus-visible .clip__knob,
.clip.is-scrubbing .clip__knob { transform: translateY(-50%) scale(1); }

.clip__bar:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; border-radius: 2px; }

.clip__time {
  flex: none;
  color: var(--on-media);
  font-size: 0.6rem;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 2px rgba(var(--scrim-rgb), 0.6);
}

@media (prefers-reduced-motion: reduce) {
  .clip__bar::before, .clip__fill, .clip__knob { transition: none; }
}

/* ----------------------------------------------------------------- process
   Numbered because this genuinely is a sequence — each stage depends on the
   one under it, which is exactly the point being made.
   ------------------------------------------------------------------------ */
.steps { counter-reset: step; display: grid; gap: 1px; background: var(--joint); border-block: 1px solid var(--joint); }
.step {
  counter-increment: step;
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: 1.5rem;
  align-items: start;
  padding: 1.9rem 0.25rem;
  background: var(--tarmac);
  transition: background-color 0.28s var(--ease);
}
.step:hover { background: var(--slate); }
.step::before {
  content: "0" counter(step);
  font-family: "JetBrains Mono", monospace;
  font-size: 1.5rem;
  font-weight: 400;
  color: var(--joint);
  line-height: 1;
  padding-top: 0.2rem;
}
.step:hover::before { color: var(--amber-lo); }
.step h3 { font-size: 1.1rem; font-variation-settings: "wdth" 110, "wght" 700; }
.step p { margin: 0.5rem 0 0; color: var(--mortar); max-width: 60ch; font-size: 0.96rem; }

@media (max-width: 620px) {
  .step { grid-template-columns: 1fr; gap: 0.4rem; }
  .step::before { font-size: 1.15rem; }
}

/* ----------------------------------------------------------------- gallery */
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.75rem;
}
.filter {
  padding: 0.55rem 1rem;
  background: transparent;
  border: 1px solid var(--joint);
  border-radius: 2px;
  color: var(--mortar);
  font-family: "JetBrains Mono", monospace;
  font-size: var(--fs-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.18s var(--ease), border-color 0.18s var(--ease), background-color 0.18s var(--ease);
}
.filter:hover { color: var(--sandstone); border-color: var(--mortar-dim); }
.filter[aria-pressed="true"] {
  background: var(--amber);
  border-color: var(--amber);
  color: var(--on-accent);
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 10px;
}
.tile {
  position: relative;
  aspect-ratio: 4 / 3;
  padding: 0;
  border: 0;
  border-radius: 2px;
  overflow: hidden;
  background: var(--granite);
  cursor: zoom-in;
}
.tile img {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: saturate(0.9);
  transition: transform 0.65s var(--ease), filter 0.4s var(--ease);
}
.tile:hover img { transform: scale(1.06); filter: saturate(1.05); }
.tile__tag {
  position: absolute;
  left: 0; bottom: 0;
  padding: 0.45rem 0.7rem;
  background: rgba(var(--scrim-rgb), 0.86);
  color: var(--on-media);
  font-family: "JetBrains Mono", monospace;
  font-size: 0.62rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  opacity: 0;
  transform: translateY(100%);
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
}
.tile:hover .tile__tag, .tile:focus-visible .tile__tag { opacity: 1; transform: translateY(0); }
.tile[hidden] { display: none; }

/* -------------------------------------------------------------- lightbox */
.lightbox {
  width: min(96vw, 1200px);
  max-width: none;
  padding: 0;
  border: 1px solid var(--joint);
  border-radius: 3px;
  background: var(--slate);
  color: var(--sandstone);
  overflow: hidden;
}
.lightbox::backdrop { background: var(--backdrop); backdrop-filter: blur(6px); }
.lightbox img { width: 100%; max-height: 82svh; object-fit: contain; background: var(--void); }
.lightbox__bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 0.9rem;
  border-top: 1px solid var(--joint);
}
.lightbox__caption { margin-right: auto; }
.lightbox__btn {
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  background: transparent;
  border: 1px solid var(--joint);
  border-radius: 2px;
  color: var(--sandstone);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
}
.lightbox__btn:hover { background: var(--granite); }
/* display:grid above outranks the UA [hidden] rule, so hiding the paging
   arrows for single-image views needs saying explicitly. */
.lightbox__btn[hidden] { display: none; }

/* ---------------------------------------------------------------- reviews
   A CSS-columns wall rather than a uniform grid — the quotes vary wildly
   in length, and forcing them to equal-height cards would waste space or
   clip text. Each card still reads as a job-sheet index card, consistent
   with the rest of the page.
   ------------------------------------------------------------------------ */
.reviews {
  column-width: 320px;
  column-gap: 10px;
}
.review {
  break-inside: avoid;
  margin-bottom: 10px;
  padding: 1.4rem 1.5rem 1.5rem;
  border: 1px solid var(--joint);
  background: var(--slate);
}
.review__quote {
  margin: 0;
  color: var(--ink-bright);
  font-size: 0.96rem;
  line-height: 1.65;
}
.review__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.1rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--joint);
}
.review__name { font-weight: 650; font-size: 0.9rem; }
.review__src { color: var(--mortar-dim); font-size: 0.62rem; white-space: nowrap; }
.review__more {
  display: inline-block;
  margin-top: 0.8rem;
  color: var(--amber);
  font-size: 0.82rem;
  text-decoration: none;
}
.review__more:hover { text-decoration: underline; }
.review--cut .review__quote { color: var(--ink-bright); }

@media (max-width: 480px) {
  .reviews { column-width: 100%; }
}

/* ------------------------------------------------------------ trade badges
   The three trade directories J2 are listed on. Only MyBuilder publishes a
   wordmark; the other two supply square icons with no name in them, so each
   card pairs the mark with the platform name set in our own type. That also
   means a missing image file degrades to a plain named card, never a gap.
   Same hairline-joint treatment as the contact channels. */
.trades {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  margin-top: 2.5rem;
  background: var(--joint);
  border: 1px solid var(--joint);
}
.trade {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 0.85rem;
  padding: 1.75rem 1.25rem 1.6rem;
  background: var(--slate);
  text-decoration: none;
  transition: background 0.2s var(--ease);
}
.trade:hover { background: var(--granite); }
.trade__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 38px;
}
/* Marks arrive at wildly different aspect ratios (32x32, 720x720, 165x127),
   so height is the constant and width follows. Left in brand colour — all
   three read cleanly on tarmac. */
.trade__mark img {
  height: 38px;
  width: auto;
  opacity: 0.92;
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
}
.trade:hover .trade__mark img { opacity: 1; transform: translateY(-2px); }
/* TopTradespeople ship their mark on a 720x720 canvas with ~10% padding
   baked in, so it optically reads small next to the other two. Nudged up
   rather than cropping their artwork. */
.trade__mark img[src$="toptradespeople.svg"] { height: 46px; }
@media (max-width: 620px) { .trade__mark img[src$="toptradespeople.svg"] { height: 36px; } }
.trade__name {
  font-variation-settings: "wdth" 112, "wght" 700;
  font-size: 1rem;
  line-height: 1.2;
  text-align: center;
}
.trade__cta { color: var(--mortar-dim); font-size: 0.66rem; }
.trade:hover .trade__cta { color: var(--amber); }

@media (max-width: 620px) {
  .trades { grid-template-columns: 1fr; }
  .trade {
    flex-direction: row;
    justify-content: flex-start;
    gap: 1rem;
    padding: 1.15rem 1.35rem;
  }
  .trade__mark, .trade__mark img { height: 30px; }
  .trade__cta { margin-left: auto; }
}

/* ------------------------------------------------------------------- areas */
.areas__grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
@media (max-width: 900px) {
  .areas__grid { grid-template-columns: 1fr; gap: 2.25rem; }
}
/* The heading now lives inside the left column so the map lines up against
   the whole block, not just the town list. Drop the standalone-heading
   measure and spacing that assumed it sat full-width above. */
.areas__col .head { max-width: none; margin-bottom: 2rem; }
.areas__col .head p { max-width: 46ch; }

.areas {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem 0;
  margin: 0;
  padding: 0;
  list-style: none;
}
.areas li {
  font-size: clamp(1rem, 0.9rem + 0.3vw, 1.15rem);
  line-height: 1.45;
  color: var(--sandstone);
}
.areas li:not(:last-child)::after {
  content: "·";
  margin: 0 0.7rem;
  color: var(--amber-lo);
}
@media (max-width: 620px) {
  .areas li:not(:last-child)::after { margin: 0 0.55rem; }
}

/* Rather than fading the map out, mount it: a dark matte and a joint-coloured
   rule give the light map an edge that belongs to the page, the same way the
   swatches and tiles are framed elsewhere. */
.areas__map { margin: 0; }
.areas__mapbtn {
  display: block;
  width: 100%;
  padding: 0.55rem;
  border: 1px solid var(--joint);
  border-radius: 3px;
  background: var(--granite);
  cursor: zoom-in;
  transition: border-color 0.25s ease, background-color 0.25s ease;
}
.areas__mapbtn img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 2px;
}
.areas__mapbtn:hover,
.areas__mapbtn:focus-visible { border-color: var(--amber-lo); background: var(--joint); }

.areas__mapcap {
  margin-top: 0.7rem;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--mortar-dim);
}

/* ----------------------------------------------------------------- contact */
.contact { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2.5rem, 6vw, 5rem); align-items: start; }
@media (max-width: 900px) { .contact { grid-template-columns: 1fr; } }

.channels { display: grid; gap: 1px; background: var(--joint); border: 1px solid var(--joint); margin-top: 2.25rem; }
.channel {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  padding: 1.15rem 1.25rem;
  background: var(--slate);
  text-decoration: none;
  transition: background-color 0.22s var(--ease);
}
.channel:hover { background: var(--granite); }
.channel svg { width: 21px; height: 21px; flex: none; color: var(--amber); }
.channel__label { display: block; }
.channel__value { display: block; font-variation-settings: "wdth" 110, "wght" 650; font-size: 1.05rem; }

.form { background: var(--slate); border: 1px solid var(--joint); padding: clamp(1.5rem, 3vw, 2.1rem); }
.form h3 { font-size: 1.2rem; font-variation-settings: "wdth" 112, "wght" 700; }
.form > p { margin: 0.6rem 0 1.6rem; color: var(--mortar); font-size: 0.94rem; }
.field { display: block; margin-bottom: 1rem; }
.field > span { display: block; margin-bottom: 0.4rem; }
.field input, .field select, .field textarea {
  width: 100%;
  padding: 0.75rem 0.85rem;
  background: var(--tarmac);
  border: 1px solid var(--joint);
  border-radius: 2px;
  color: var(--sandstone);
  font: inherit;
  font-size: 0.95rem;
}
.field textarea { resize: vertical; min-height: 96px; }
.field input::placeholder, .field textarea::placeholder { color: var(--mortar-dim); }
.form__actions { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 1.35rem; }
.form__note { margin: 1rem 0 0; font-size: 0.82rem; color: var(--mortar-dim); }

/* ------------------------------------------------------------------ footer */
.footer { position: relative; padding-block: 3rem 7.5rem; border-top: 1px solid var(--joint); }
@media (min-width: 901px) { .footer { padding-bottom: 3rem; } }
.footer__grid { display: flex; flex-wrap: wrap; gap: 1.5rem 1.5rem; align-items: center; }
.footer__mark { width: 46px; height: 46px; flex: none; color: var(--mortar); }
.footer__grid p { margin: 0; color: var(--mortar-dim); font-size: 0.86rem; }
.footer__links { display: flex; flex-wrap: wrap; gap: 0.6rem 1.4rem; margin-left: auto; }
.footer__links a { color: var(--mortar); font-size: 0.86rem; text-decoration: none; }
.footer__links a:hover { color: var(--sandstone); }

/* ------------------------------------------------------- sticky call bar */
.callbar {
  position: fixed;
  inset: auto 0 0;
  z-index: 70;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--joint);
  border-top: 1px solid var(--joint);
}
.callbar a {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  padding: 0.95rem;
  background: var(--slate);
  color: var(--sandstone);
  font-weight: 650;
  font-size: 0.95rem;
  text-decoration: none;
}
.callbar a:first-child { background: var(--amber); color: var(--on-accent); }
.callbar svg { width: 17px; height: 17px; }
@media (min-width: 901px) { .callbar { display: none; } }

/* --------------------------------------------------------------- reveals
   Tiles arrive in stagger, top-left to bottom-right — the order blocks go
   down on a real job.
   ------------------------------------------------------------------------ */
/* Scoped to .js so the page is fully readable if scripting is unavailable —
   otherwise every section would sit at opacity 0 forever. */
.js .rise {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
  transition-delay: var(--d, 0ms);
}
.js .rise.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .js .rise { opacity: 1; transform: none; transition: none; }
  .svc__media img, .tile img { transition: none; }
  .svc:hover .svc__media img, .tile:hover img { transform: none; }
  .btn:hover { transform: none; }
}

/* =========================================================================
   ALTERNATE PALETTES
   Set data-theme on <html> to swap -- see the note above <html> in index.html.
   "brand" is live as of 2026-08-23; the others are kept for future use. Every
   value below is sampled from Logos/j2LOGO.jpg. Nothing outside these blocks is
   theme-specific, so removing the attribute returns the site to the original
   charcoal palette exactly.
   ========================================================================= */

/* Dark navy + silver — the logo's shield colours, same weight as the default. */
[data-theme="brand"] {
  --tarmac:    #0c121e;
  --slate:     #131b2b;
  --granite:   #1c2739;
  --joint:     #2c3a51;

  --sandstone: #e4ecf5;
  --mortar:    #93a4bb;
  --mortar-dim:#68798f;

  --amber:     #bcccdc;   /* the logo's pale blue, doing the accent's job */
  --amber-lo:  #7d90a8;
  --amber-hi:  #d5e2ef;
  --on-accent: #0c121e;

  --scrim-rgb:  12, 18, 30;
  --header-rgb: 12, 18, 30;
  --on-media-accent: #bcccdc;
  --on-media-rgb: 228, 236, 245;

  --ink-soft:   #c3cfdd;
  --ink-bright: #dce6f1;
  --void:     #070b13;
  --backdrop: rgba(6, 9, 15, 0.9);
}

/* White + navy — the logo's background and lettering, inverted to a light page. */
[data-theme="light"] {
  --tarmac:    #eef2f7;
  --slate:     #e4ebf3;
  --granite:   #ffffff;
  --joint:     #c6d3e1;

  --sandstone: #16202f;
  --mortar:    #4d5d73;
  --mortar-dim:#78889d;

  --amber:     #24364e;   /* navy carries the accent on a light page */
  --amber-lo:  #46628a;
  --amber-hi:  #2f4a6b;
  --on-accent: #eef2f7;

  /* Scrims deliberately stay dark: photographs still need light text on them. */
  --header-rgb: 238, 242, 247;
  --sheen-rgb:  12, 18, 30;   /* the grain speckle inverts to dark-on-light */
  --sheen-a: 0.045;
  --ghost-a: 0.06;

  --on-media-accent: #bcccdc;
  --nav-ink:    #eaf0f7;
  --nav-ink-hi: #ffffff;

  --ink-soft:   #3a4a61;
  --ink-bright: #16202f;
  --void:     #0d1420;
  --backdrop: rgba(10, 16, 26, 0.9);
}


/* Privacy policy link in the footer. Deliberately quiet -- it needs to be
   present and findable, not competing with the contact links above it.
   flex-basis:100% drops it onto its own line below the rest of the footer
   row; the selector is doubled up so it beats ".footer__grid p" above. */
.footer__grid .footer__legal {
  flex: 0 0 100%;
  margin: 0.5rem 0 0;
  font-size: 0.78rem;
}
.footer__legal a { color: var(--mortar-dim); text-decoration: none; }
.footer__legal a:hover { color: var(--mortar); text-decoration: underline; }
