/* ==========================================================================
   Construction page, below the hero (contracting.html only)
   --------------------------------------------------------------------------
   contracting.html loads this after css/editorial.css, and no other page
   loads it at all. Every selector hangs off a cx- class that exists only in
   that page's markup, so nothing here can reach the hero (.con-hero, whose
   rules stay in editorial.css), the header, the footer or another page.

   Below the hero, in order:
     1  .cx-services  bone   five service columns; a ruled list on tablets,
            a stack of photo cards from 768px down
     2  .cx-projects  ink    a three-photo grid, then the full gallery on demand
     3  .cx-process   bone   four steps
     4  .cx-cta       photo  the close, on a framing photo under a dark fade

   It borrows the editorial layer's voice rather than adding one: the kicker,
   the two-voice headline, the lede, the buttons, the type ladder and the one
   easing curve. Nothing here sets a size of its own — every heading, title
   and caption picks a step off the ladder in css/styles.css, so a service
   title here matches a service title on the homepage. The one thing that is
   new is the measure: the photo grids run wider (.cx-wide) than the text on
   large screens, edge to edge in rows of their own.
   ========================================================================== */

/* ──────────────────────────────────────────────── Shell */
/* A tighter rhythm than the editorial layer's --sect-y (112px at desktop):
   72px here, as in the reference, so four sections read as one page. */
.ed .cx-sect {
  --cx-pad: clamp(3.25rem, 5vw, 4.5rem);
  --cx-head-gap: clamp(1.6rem, 2.6vw, 2.4rem);
  padding-block: var(--cx-pad);
}

/* The photo measure. Up to 1279px it is the text column exactly, so photos
   and text share both edges. From 1280px it opens to 1440px, so the photos
   run 50px past the text each side at 1280 and 130px at 1440 and up. It
   steps rather than grows, because a bleed of only a few pixels reads as a
   misalignment rather than a decision. */
.ed .cx-wide {
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: clamp(20px, 4.5vw, 34px);
}
@media (min-width: 1280px) {
  .ed .cx-wide { max-width: 1440px; }
}

/* ──────────────────────────────────────────────── Section head
   Kicker and headline on the left, a short intro on the right, the two
   bottoms lined up. Stacks under 900px. */
.ed .cx-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: end;
  gap: 1.1rem clamp(2rem, 5vw, 4.5rem);
  margin-bottom: var(--cx-head-gap);
}
.ed .cx-head .h-kicker { margin-bottom: 0.95rem; }

.ed .cx-head__aside { max-width: 33rem; }
.ed .cx-head__aside.h-lede,
.ed .cx-head__aside .h-lede {
  margin: 0;
  text-wrap: pretty;
}
.ed .cx-head__aside .btn { margin-top: 1.4rem; }

@media (max-width: 899px) {
  .ed .cx-head { grid-template-columns: minmax(0, 1fr); align-items: start; gap: 1rem; }
}

/* ──────────────────────────────────────────────── 1 · Services
   Five equal columns. The photos are one 4:3 frame each, edge to edge with
   a narrow gap; under them a title, one line of copy and an arrow pinned to
   the bottom so all five arrows share a line. The 4:3 frame is the constant:
   five of them across a desktop, one across a phone. Hairlines in the gaps run
   beside the text only, never between the photos. Each column is a single
   link: the title's link stretches over the whole column. */
.ed .cx-svcs {
  --cx-cg: clamp(14px, 1.3vw, 20px);         /* column gap; the hairline sits in its middle */
  --cx-in: clamp(0.35rem, 0.7vw, 0.65rem);   /* text inset from the photo's edge */
  --cx-top: clamp(1.1rem, 1.7vw, 1.45rem);   /* photo to title */
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  column-gap: var(--cx-cg);
  margin: 0;
  padding: 0;
}
.ed .cx-svc {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
/* The photo comes after the text in the source, so a screen reader hears
   the service before the description of its photo; order puts it on top. */
.ed .cx-svc__fig {
  order: -1;
  margin: 0;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: 2px;
  background: #E6E2D9;
}
.ed .cx-svc__fig img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 50%);
  transition: transform .8s var(--ease);
}
.ed .cx-svc__body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  padding: var(--cx-top) var(--cx-in) 0;
}
.ed .cx-svc + .cx-svc .cx-svc__body {
  margin-left: calc(var(--cx-cg) / -2);
  padding-left: calc(var(--cx-cg) / 2 + var(--cx-in));
  background: linear-gradient(var(--l-line), var(--l-line)) left bottom / 1px calc(100% - var(--cx-top)) no-repeat;
}
.ed .cx-svc__t {
  margin: 0;
  font-family: var(--font-head);
  font-size: var(--fs-h6);
  font-weight: 700;
  line-height: var(--lh-head);
  letter-spacing: var(--ls-head-sm);
  text-transform: uppercase;
  color: #141414;
}
.ed .cx-svc__a {
  color: inherit;
  text-decoration: none;
  transition: color .3s var(--ease);
}
.ed .cx-svc__a::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}
.ed .cx-svc__a:focus-visible { outline: none; }
.ed .cx-svc__a:focus-visible::after { outline: 2px solid var(--o); outline-offset: 4px; }
.ed .cx-svc__b {
  margin: 0.5rem 0 1.1rem;
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  color: var(--txt);
}
.ed .cx-svc__arrow {
  flex: none;
  margin-top: auto;
  color: var(--o-serif);
  transition: transform .35s var(--ease);
}

@media (hover: hover) {
  .ed .cx-svc:hover .cx-svc__fig img { transform: scale(1.045); }
  .ed .cx-svc:hover .cx-svc__a { color: var(--o-deep); }
  .ed .cx-svc:hover .cx-svc__arrow { transform: translateX(6px); }
}
.ed .cx-svc:focus-within .cx-svc__arrow { transform: translateX(6px); }

/* Tablets, 769 to 1023px: five columns would be too narrow to read, so the
   section becomes a ruled list: photo on the left, text in the middle, arrow
   on the right, a hairline between rows. The photo still holds a quarter of
   the row here, which is enough to read a job by. */
@media (min-width: 769px) and (max-width: 1023px) {
  .ed .cx-svcs { grid-template-columns: minmax(0, 1fr); border-top: 1px solid var(--l-line); }
  .ed .cx-svc {
    flex-direction: row;
    align-items: center;
    gap: clamp(1rem, 3vw, 1.75rem);
    padding-block: clamp(0.9rem, 2.2vw, 1.2rem);
    border-bottom: 1px solid var(--l-line);
  }
  .ed .cx-svc__fig { flex: 0 0 clamp(7.25rem, 22vw, 12rem); }
  .ed .cx-svc__body,
  .ed .cx-svc + .cx-svc .cx-svc__body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    column-gap: 1rem;
    margin: 0;
    padding: 0;
    background: none;
  }
  .ed .cx-svc__t,
  .ed .cx-svc__b { grid-column: 1; }
  .ed .cx-svc__b { margin-bottom: 0; }
  .ed .cx-svc__arrow { grid-column: 2; grid-row: 1 / span 2; margin: 0; }
}

/* 768px and down: beside a line of copy the photo came out barely wider than
   a thumbnail, too small to show the work. Each service stacks instead — the
   photo across the full column in the same 4:3 frame the desktop columns
   use, the title and its arrow on the line under it, then the copy. It is
   the desktop column, one to a row: same frame, same crop, same type, no
   hairlines to box it in, and the whole card stays one link. */
@media (max-width: 768px) {
  .ed .cx-svcs {
    --cx-top: clamp(0.85rem, 2.8vw, 1.05rem);
    grid-template-columns: minmax(0, 1fr);
    row-gap: clamp(1.9rem, 5.5vw, 2.5rem);
  }
  .ed .cx-svc__fig { width: 100%; }
  .ed .cx-svc__body,
  .ed .cx-svc + .cx-svc .cx-svc__body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    column-gap: 0.85rem;
    margin: 0;
    padding: var(--cx-top) 0 0;
    background: none;
  }
  .ed .cx-svc__t { grid-column: 1; }
  .ed .cx-svc__arrow { grid-column: 2; grid-row: 1; margin: 0; }
  .ed .cx-svc__b {
    grid-column: 1 / -1;
    margin: 0.35rem 0 0;
  }

  /* Stacked, the list runs several screens tall, and the scroll reveal in
     js/main.js only fires once 12% of an element is in view — a share that
     a short landscape phone can never show, which would leave the section
     at opacity 0 for good. The list simply shows here; the section head
     above it still carries the fade. */
  .ed .cx-svcs.reveal {
    opacity: 1;
    transform: none;
  }
}

/* ──────────────────────────────────────────────── 2 · Featured projects
   The cottage takes three fifths of the width and the height of two rows;
   the kitchen and the deck stack beside it. The two small frames hold the
   panorama's own 2.16:1, so the deck is shown whole and the grid's height
   follows from them. That makes the cottage about 1.58:1 at every desktop
   width, which keeps the whole house in frame; each figure's inline --pos
   says where the frame sits on its photo. Dark only at the foot, behind the
   caption. */
.ed .cx-feat {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  gap: clamp(10px, 1vw, 14px);
}
.ed .cx-feat__item {
  position: relative;
  margin: 0;
  overflow: hidden;
  aspect-ratio: 2.1645;
  border-radius: 2px;
  background: var(--d-soft);
}
.ed .cx-feat__item--lg { grid-row: span 2; aspect-ratio: auto; }
.ed .cx-feat__item img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 50%);
  transition: transform .9s var(--ease);
}
.ed .cx-feat__item::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to top,
    rgba(8, 6, 5, 0.82) 0,
    rgba(8, 6, 5, 0.5) 64px,
    rgba(8, 6, 5, 0.16) 118px,
    rgba(8, 6, 5, 0) 168px);
}
.ed .cx-feat__cap {
  position: absolute;
  z-index: 1;
  left: clamp(1rem, 1.7vw, 1.6rem);
  right: clamp(1rem, 1.7vw, 1.6rem);
  bottom: clamp(0.9rem, 1.5vw, 1.35rem);
  font-size: var(--fs-xs);
  line-height: var(--lh-sm);
  color: rgba(255, 255, 255, 0.8);
}
.ed .cx-feat__t {
  display: block;
  margin-bottom: 0;
  font-family: var(--font-head);
  font-size: var(--fs-h6);
  font-weight: 700;
  line-height: var(--lh-head);
  letter-spacing: var(--ls-head-sm);
  text-transform: uppercase;
  color: #fff;
}

@media (hover: hover) {
  .ed .cx-feat__item:hover img { transform: scale(1.035); }
}

/* Tablets: the cottage across the top, the other two side by side. */
@media (max-width: 1023px) {
  .ed .cx-feat { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .ed .cx-feat__item { aspect-ratio: 3 / 2; }
  .ed .cx-feat__item--lg { grid-column: 1 / -1; grid-row: auto; aspect-ratio: 2 / 1; }
}
/* Phones: one column, one frame for all three. */
@media (max-width: 639px) {
  .ed .cx-feat { grid-template-columns: minmax(0, 1fr); }
  .ed .cx-feat__item,
  .ed .cx-feat__item--lg { aspect-ratio: 16 / 10; }
}

/* The gallery toggle's arrow turns to point up while the gallery is open. */
.ed .cx-gallery-toggle svg { transition: transform .35s var(--ease); }
.ed .cx-gallery-toggle[aria-expanded="true"] svg { transform: rotate(-90deg); }

/* ──────────────────────────────────────────────── 2b · The full gallery
   Hidden until "View Full Gallery" opens it (js/construction.js; without
   scripting it is simply shown). Inside is the editorial layer's .ph photo
   set: rows composed by hand, every photo at its own proportions. */
.ed .cx-gallery {
  margin-top: clamp(3rem, 6vw, 4.5rem);
  padding-top: clamp(2rem, 4vw, 3rem);
  border-top: 1px solid var(--d-line);
}
.ed .cx-gallery.is-open { animation: cx-rise .7s var(--ease) both; }
.ed .cx-gallery__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.25rem 2rem;
}
.ed .cx-gallery .ph { margin-top: clamp(1.75rem, 3.4vw, 2.5rem); }
.ed .cx-gallery__foot {
  display: flex;
  justify-content: center;
  margin-top: clamp(2rem, 4vw, 3rem);
}
@keyframes cx-rise {
  from { opacity: 0; transform: translateY(16px); }
}

/* ──────────────────────────────────────────────── 3 · Process
   Four steps across, each a thin orange ring with its number and the copy
   beside it, hairlines in the gaps. Two by two on tablets; on phones a
   single column with a faint line joining the rings. */
.ed .cx-steps {
  --cx-sg: clamp(1.5rem, 3.2vw, 3rem);
  --cx-ring: 3.25rem;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--cx-sg);
  margin: 0;
  padding: 0;
}
.ed .cx-step {
  display: flex;
  align-items: flex-start;
  gap: clamp(0.85rem, 1.3vw, 1.15rem);
  min-width: 0;
}
.ed .cx-step + .cx-step {
  margin-left: calc(var(--cx-sg) / -2);
  padding-left: calc(var(--cx-sg) / 2);
  border-left: 1px solid var(--l-line);
}
.ed .cx-step__n {
  flex: none;
  display: grid;
  place-items: center;
  width: var(--cx-ring);
  height: var(--cx-ring);
  border: 1.5px solid var(--o);
  border-radius: 50%;
  font-family: var(--font-head);
  font-size: var(--fs-h6);
  font-weight: 600;
  line-height: 1;               /* a numeral centred in its ring */
  letter-spacing: var(--ls-wide);
  color: var(--o-deep);   /* small text on bone: the AA orange */
}
.ed .cx-step__body { min-width: 0; padding-top: 0.3rem; }
.ed .cx-step__t {
  margin: 0;
  font-family: var(--font-head);
  /* Four steps across: the card step, as the five service titles above use,
     so a two-word step title holds one line at every desktop width. */
  font-size: var(--fs-h6);
  font-weight: 700;
  line-height: var(--lh-head);
  letter-spacing: var(--ls-head-sm);
  text-transform: uppercase;
  color: #141414;
}
.ed .cx-step__b {
  margin: 0.45rem 0 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  color: var(--txt);
}

/* Four steps across, minus a 52px ring each, leave about 150px of text at
   1024px — four pixels short of the longest step title. Between the four-up
   grid's own breakpoint and the width where the card step fits, the title
   takes the step below, so all four titles hold one line and their copy
   starts on one baseline. */
@media (min-width: 1024px) and (max-width: 1100px) {
  .ed .cx-step__t { font-size: var(--fs-btn); }
}

@media (max-width: 1023px) {
  .ed .cx-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 2.25rem; }
  .ed .cx-step:nth-child(odd) { margin-left: 0; padding-left: 0; border-left: 0; }
}
@media (max-width: 639px) {
  .ed .cx-steps { grid-template-columns: minmax(0, 1fr); row-gap: 0; }
  .ed .cx-step,
  .ed .cx-step + .cx-step {
    position: relative;
    margin-left: 0;
    padding: 0 0 1.75rem;
    border-left: 0;
  }
  .ed .cx-step:last-child { padding-bottom: 0; }
  .ed .cx-step:not(:last-child)::before {
    content: "";
    position: absolute;
    left: calc(var(--cx-ring) / 2 - 0.5px);
    top: calc(var(--cx-ring) + 0.45rem);
    bottom: 0.45rem;
    width: 1px;
    background: rgba(243, 107, 33, 0.38);
  }
}

/* ──────────────────────────────────────────────── 4 · The close
   A framing photo under a fade that is darkest behind the headline and
   still dark enough behind the buttons for white text to clear AA. */
.ed .cx-cta {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding-block: clamp(3.25rem, 6vw, 5rem);
  background: #0E0D0C;
}
.ed .cx-cta__photo {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 36%;
}
.ed .cx-cta::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(90deg,
    rgba(10, 9, 8, 0.92) 0%,
    rgba(10, 9, 8, 0.84) 34%,
    rgba(10, 9, 8, 0.56) 66%,
    rgba(10, 9, 8, 0.44) 100%);
}
.ed .cx-cta__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1.75rem 3rem;
}
.ed .cx-cta__actions {
  display: grid;
  gap: 12px;
  width: min(100%, 17.5rem);
}
.ed .cx-cta__actions .btn { width: 100%; }

/* Stacked, the headline runs most of the way across, into the lighter side
   of the fade, so the fade evens out to hold the orange line at 3:1. */
@media (max-width: 767px) {
  .ed .cx-cta::before { background: linear-gradient(180deg, rgba(10, 9, 8, 0.88), rgba(10, 9, 8, 0.78)); }
  .ed .cx-cta__inner { flex-direction: column; align-items: flex-start; }
  .ed .cx-cta__actions { width: 100%; grid-template-columns: repeat(auto-fit, minmax(13.5rem, 1fr)); }
}

/* ──────────────────────────────────────────────── Motion */
@media (prefers-reduced-motion: reduce) {
  .ed .cx-svc__fig img,
  .ed .cx-feat__item img,
  .ed .cx-svc__arrow,
  .ed .cx-gallery-toggle svg { transition: none; }
  .ed .cx-svc:hover .cx-svc__fig img,
  .ed .cx-feat__item:hover img,
  .ed .cx-svc:hover .cx-svc__arrow,
  .ed .cx-svc:focus-within .cx-svc__arrow { transform: none; }
  .ed .cx-gallery.is-open { animation: none; }
}
