/*
 * Published contractor site styles.
 *
 * The single source of visual truth. SectionRenderer emits the class names
 * below, and the builder previews pages through an iframe of that same
 * renderer — so there is exactly one place where a section's look is defined.
 *
 * Extracted from the Claude Design prototypes in design/*.dc.html.
 */

:root {
  --bg: #faf8f3;
  --bg-alt: #f2ede2;
  --dark: #262019;
  --dark-2: #2f2820;
  --text: #2b2521;
  --muted: #6f6558;
  /* Darker muted, for lead paragraphs that sit at larger sizes and carry real
     content. 7.3:1 on --bg-alt against --muted's 4.9:1. */
  --muted-strong: #554b40;
  --accent: #8a6540;
  --accent-dark: #6d4e2f;
  --gold: #c9a877;
  --gold-bright: #e8b34b;
  --border: #e8e0d2;
  --border-2: #d8cebc;
  --on-dark: #faf8f3;
  --on-dark-muted: #ddd3c3;
  --radius: 12px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* One background for the whole site. Sections no longer alternate between
     two creams; the divider rule below is what separates them, and the only
     band that breaks the field is the dark one behind the reviews. */
  background: var(--bg-alt);
  color: var(--text);
  font-family: 'Karla', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  -webkit-font-smoothing: antialiased;
  /* Sticky footer: on a short page — a service with one subservice, say — the
     footer would otherwise float mid-screen with background below it. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

main { flex: 1 0 auto; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-dark); }
img { max-width: 100%; display: block; }

h1, h2, h3, .serif {
  font-family: 'Marcellus', Georgia, serif;
  font-weight: 400;
  margin: 0;
}

.container { max-width: 1180px; margin: 0 auto; padding: 0 24px; }
/* Even and tight, top and bottom. The divider at the top of each band is what
   separates one section from the next, so the padding only has to keep the
   content off the line rather than do the separating itself. */
.section { padding: 25px 0; }
.section--alt { background: var(--bg-alt); }

/* One divider for the whole site: a hairline the width of the container,
   drawn at the top of every band that follows another. Drawing it on the
   following band rather than the preceding one means there is never a line
   left hanging after the last section on a page.
   .band marks a full-width section wrapper; article blocks inside a blog post
   do not carry it, so paragraphs are not sliced apart by rules. */
.band + .band::before {
  content: ""; display: block; height: 1px; background: var(--border-2);
  width: min(100% - 48px, 1132px); margin: 0 auto;
}
/* Two places where a line would be noise rather than structure: under the
   cover, which already ends in a large dark field, and against the reviews
   band, which is a field of colour whose own edges do the separating. */
.band:has(> .hero) + .band::before,
.band:has(> .section--reviews) + .band::before,
.band:has(> .section--reviews)::before { display: none; }

.eyebrow {
  font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent); font-weight: 700; margin-bottom: 10px;
}
/* Marcellus has a single 400 cut, so asking it for bold gives a smeared faux
   bold. Section titles use Karla 800 instead: a real heavy weight, already
   loaded. The serif is kept for the hero and card titles, so the page reads as
   display serif + strong sans section headers rather than one flat voice. */
.section-title { font-family: 'Karla', system-ui, sans-serif; font-weight: 800;
                 font-size: clamp(32px, 4vw, 46px); letter-spacing: -.015em; }
/* Centred: the hero copy and the cards below are centred, so a left-aligned
   head was the only thing breaking the column. */
.section-head { margin-bottom: 40px; text-align: center; }
.section-lead { font-size: 17px; line-height: 1.6; color: var(--muted-strong); max-width: 62ch; margin: 12px auto 0; }

/* Text the contractor typed is shown as they typed it.
 *
 * HTML collapses runs of spaces and ignores newlines, so an indent or a blank
 * line between paragraphs in the builder simply vanished on the page -- the
 * box said one thing and the site said another, with nothing to explain the
 * difference. pre-wrap keeps both and still wraps normally at the edge.
 *
 * Only on fields someone types into. Review text is left alone: it comes from
 * Google, and whatever spacing a stranger used is not a layout instruction. */
.section-lead,
.card-text,
.detail-text,
.founder-bio,
.process-step-text,
.cta-sub,
.verdict-item p,
/* The hero subheadline has no class of its own -- it is the paragraph that
   follows the headline inside the copy block. */
.hero-copy p { white-space: pre-wrap; }

/* The band that opens a page. Its heading is the page's title, so it runs a
   little larger than the section heads below it, and its intro is given the
   width of the page rather than the 62ch reading measure -- a deliberate
   choice here, where the paragraph is the content, rather than a caption
   sitting above a grid. */
.section--intro .section-title { font-size: clamp(35px, 4.4vw, 51px); }
.section--intro .section-lead { max-width: 90%; }

/* With a background photo the band behaves like the cover: the image fills it,
   a scrim holds the contrast, and the container is lifted above both. The
   band keeps a little more air than a plain one, because type sitting tight
   against the edge of a photograph looks like a mistake. */
.section--intro-photo { position: relative; background: var(--dark); padding: 40px 0; }
/* Tighter under the heading too: on a photo the pair reads as one block, and
   the wide gap the plain band uses pulls them apart. */
.section--intro-photo .section-head { margin-bottom: 22px; }
.section--intro-photo .intro-bg { position: absolute; inset: 0; }
.section--intro-photo .intro-scrim { position: absolute; inset: 0; pointer-events: none;
                                     background: rgba(38, 32, 25, .58); }
.section--intro-photo > .container { position: relative; }
/* Larger than a plain intro's heading: a photographic band is a bigger field
   and the same size reads as small against it. The lead has to move with it --
   the dark text it wears on cream disappears on a photo. */
.section--intro-photo .section-title { font-size: clamp(42px, 5.3vw, 61px);
                                       color: var(--on-dark); }
/* Full strength, not the muted cream: over a photograph the muted tone loses
   against the bright parts of the image, and this is the one paragraph on the
   page that has to be read. */
/* Centred under a centred heading. The intro's lead is rendered outside the
   .section-head block -- that is what makes it range left here while every
   other band's does not -- so the alignment has to be stated. */
.section--intro-photo .section-lead { color: var(--on-dark); font-size: 18.5px;
                                      text-align: center; max-width: 70ch; }

/* One button for the whole site, the brown from the contact prompt. The cover
   is the single exception below: it sits on a dark photograph, where brown on
   near-black would barely read as a button at all. */
.btn {
  /* 10% up on the original 15/28 padding and 16px label. */
  display: inline-block; padding: 16.5px 30.8px; border-radius: 7px;
  font-weight: 600; font-size: 17.6px; border: none; cursor: pointer;
  background: var(--accent); color: var(--on-dark);
}
.btn:hover { background: var(--accent-dark); color: var(--on-dark); }
.btn--hero { background: var(--gold); color: var(--dark); font-weight: 700; }
.btn--hero:hover { background: var(--gold-bright); color: var(--dark); }
.btn--ghost { background: transparent; border: 1.5px solid rgba(243,237,226,0.55); color: var(--on-dark); font-weight: 600; }
.btn--ghost:hover { border-color: var(--on-dark); color: var(--on-dark); }

/* An image that hasn't been uploaded yet — a quiet placeholder, never a broken icon. */
.media { position: relative; overflow: hidden; background: var(--bg-alt); }
.media img { width: 100%; height: 100%; object-fit: cover; }
.media--empty {
  background: repeating-linear-gradient(45deg, #ece5d7 0 10px, #e4dccb 10px 20px);
}
.media--contain img { object-fit: contain; }

/* ---- site chrome ---- */

.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(242,237,226,0.96); backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}
.site-header .container {
  height: 68px; display: flex; align-items: center; justify-content: space-between; gap: 24px;
}
.brand { font-family: 'Marcellus', Georgia, serif; font-size: 21px; letter-spacing: .04em; color: var(--text); white-space: nowrap; }
/* Header lockup: a square-ish mark beside the business name. Sizing the mark
   alone keeps it legible — a wide logo with a tagline baked in has to shrink
   to the bar height and its small type becomes unreadable. */
.brand { display: inline-flex; align-items: center; gap: 12px; }
.brand-mark { display: block; height: 38px; width: auto; max-width: 56px; object-fit: contain; }
.brand-name { font-family: 'Marcellus', Georgia, serif; font-size: 21px; letter-spacing: .04em;
              color: var(--text); white-space: nowrap; }
.site-nav { display: flex; align-items: center; gap: 28px; font-size: 15px; font-weight: 500; }
.site-nav a { color: var(--text); }
.site-nav a.is-active { color: var(--accent); font-weight: 700; }
.nav-toggle { display: none; background: none; border: none; cursor: pointer; flex-direction: column; gap: 5px; padding: 8px; }
.nav-toggle span { display: block; width: 24px; height: 2px; background: var(--text); }

.site-footer { background: var(--dark); color: var(--on-dark-muted); padding: 56px 0 32px; }
.site-footer a { color: var(--on-dark-muted); }
.site-footer a:hover { color: var(--on-dark); }
.footer-grid { display: grid; grid-template-columns: 1.6fr 1fr; gap: 40px; }
/* The mark sits beside the name, as it does in the header. */
/* The name carries the footer; the blurb under it stays at its old size so it
   reads as a caption rather than competing with the name, and the contact
   details sit between the two. */
.footer-brand { font-family: 'Marcellus', Georgia, serif; font-size: 30px; color: var(--on-dark);
                margin-bottom: 16px; display: flex; align-items: center; gap: 16px; }
.footer-mark { height: 51px; width: auto; max-width: 78px; object-fit: contain; }
.footer-blurb { font-size: 14.5px; line-height: 1.6; max-width: 46ch; }
.footer-head { font-size: 15px; letter-spacing: .1em; text-transform: uppercase; color: var(--gold); font-weight: 700; margin-bottom: 14px; }
.footer-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; font-size: 16.5px; }
/* Icon and text on a fixed two-column rail, so a wrapping address lines up
   under itself rather than running back beneath its own icon. */
.footer-contact li { display: grid; grid-template-columns: 20px 1fr; gap: 11px; align-items: start; }
.footer-icon { width: 20px; height: 20px; margin-top: 1px; fill: none; stroke: var(--gold);
               stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }

.footer-sep { margin: 0 10px; opacity: .45; }
.footer-bottom { border-top: 1px solid #3d3428; margin-top: 40px; padding-top: 20px; font-size: 14.5px; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px; }

/* ---- hero ---- */

.hero { position: relative; min-height: 560px; display: flex; align-items: center; background: var(--dark); }
.hero .media { position: absolute; inset: 0; }
/* Even overlay rather than a left-weighted gradient: hero copy is centred, and
   a one-sided scrim reads lopsided behind it. */
.hero-scrim { position: absolute; inset: 0; pointer-events: none; background: rgba(38, 32, 25, .58); }
.hero-inner { position: relative; width: 100%; padding-top: 96px; padding-bottom: 96px; }
.hero-copy { max-width: 620px; }
/* Split motto: text, mark, text on one line. The halves share the leftover
   space equally and lean towards the mark, so it reads as one phrase with the
   logo set into the middle rather than three separate things. */
.hero-copy--wide { max-width: 1120px; }
.hero-split { display: flex; align-items: center; justify-content: center;
              gap: clamp(14px, 3vw, 44px); flex-wrap: nowrap; }
.hero-split-text { flex: 1 1 0; min-width: 0; }
.hero-split-text:first-child { text-align: right; }
.hero-split-text:last-child  { text-align: left; }
.hero-split-logo { flex: 0 0 auto; height: clamp(120px, 19vw, 260px); width: auto;
                   object-fit: contain; }
.hero h1 { font-size: clamp(36px, 5vw, 56px); line-height: 1.1; margin: 0 0 20px; color: var(--on-dark); }
.hero p { font-size: 18px; line-height: 1.6; color: var(--on-dark-muted); margin: 0 0 32px; max-width: 52ch; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 14px; }
/* Years-of-experience badge: a house silhouette over a ribbon, pinned to the
   bottom-left of the cover. The shape is a clip-path rather than an image so
   it stays sharp at any size and the numbers remain selectable text. */
.hero-badge { position: absolute; left: clamp(14px, 4vw, 56px); bottom: 5px; z-index: 2;
              width: clamp(75px, 7.6vw, 105px); text-align: center; }
/* Steeper roof than a plain pentagon, matching the reference badge. */
.hero-badge-shape { background: var(--on-dark); color: var(--text);
                    clip-path: polygon(50% 0, 100% 32%, 100% 100%, 0 100%, 0 32%);
                    padding: clamp(19px, 2.3vw, 27px) 7px clamp(8px, .85vw, 11px); }
/* Heavy sans, not the page serif: the number is a figure to read at a glance,
   not a piece of prose. Karla is already loaded for body text. */
.hero-badge-num { display: block; font-family: 'Karla', system-ui, sans-serif;
                  font-weight: 700; font-size: clamp(27px, 3.2vw, 41px); line-height: .95;
                  letter-spacing: -.02em; }
.hero-badge-label { display: block; margin-top: 2px;
                    font-family: 'Karla', system-ui, sans-serif; font-weight: 800;
                    font-size: clamp(11px, 1.05vw, 13px);
                    letter-spacing: .18em; text-transform: uppercase; }
/* Wider than the shape and overhanging it, so it reads as a ribbon wrapped
   around the badge rather than a third stacked block. */
.hero-badge-ribbon { background: var(--gold); color: var(--on-dark);
                     margin: 0 clamp(-9px, -.72vw, -6px);
                     padding: clamp(4px, .6vw, 7px) 4px;
                     font-family: 'Karla', system-ui, sans-serif;
                     font-size: clamp(9.5px, .92vw, 11.5px); font-weight: 800;
                     letter-spacing: .16em; text-transform: uppercase; }
/* Optional hero logo. Sized by height so a wide mark and a square one both land
   at a sensible size, capped by width so a very wide logo cannot overflow on a
   phone. object-fit keeps the aspect ratio of whatever was uploaded. */
.hero-logo { display: block; margin: 0 auto 28px; height: clamp(72px, 12vw, 160px);
             max-width: min(420px, 80%); width: auto; object-fit: contain; }

/* ---- card grids ---- */

.grid { display: grid; gap: 24px; }
/* Column count comes from the renderer so rows stay even. minmax(0,1fr) rather
   than 1fr, or a long word in a card would push the track wider than its share. */
/* align-items:start matters: grid items stretch to the row height by default,
   which would override the card's aspect-ratio. */
.grid--services { grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)); align-items: start; }
/* Landscape cards -- 5:4 keeps the height below the width at any column count --
   split 60% image, 40% copy. aspect-ratio gives the fr units a definite height
   to divide without pinning a pixel value that only suits one breakpoint. */
.grid--services .card { display: grid; grid-template-rows: 6fr 4fr; overflow: hidden;
                        aspect-ratio: 5 / 4; }
.grid--services .card .media { height: auto; min-height: 0; }
.grid--services .card-body { min-height: 0; justify-content: center; padding: 12px 18px 16px; gap: 6px; }
.grid--services .card-title { font-size: 22px; line-height: 1.2; }
/* Balanced rows like the service grid. The gaps are half what they were --
   the photos keep a tenth of the row's width between them rather than a fifth
   -- and the photos take back everything the gaps gave up, so the row still
   begins and ends flush with the section above it. */
.grid--materials { grid-template-columns: repeat(var(--cols, 4), minmax(0, 1fr));
                   column-gap: calc(10% / (var(--cols, 4) - 1) + 8px); row-gap: 28px; }
@media (max-width: 820px) {
  /* Two across, and the photos take their whole track again. Narrowing them
     to leave air is a luxury of a wide screen; on a tablet or a phone the
     photo is the thing worth the space. */
  .grid--materials { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 28px; }
}
@media (max-width: 520px) {
  /* One per row: there is no neighbour left to make room for. */
  .grid--materials { grid-template-columns: minmax(0, 1fr); column-gap: 0; }
}
.grid--reviews { grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); align-items: start; }
.grid--projects { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid--founders { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.grid--posts { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }

.card {
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; color: var(--text); display: flex; flex-direction: column;
}
.card:hover { border-color: var(--border-2); color: var(--text); }
.card .media { height: 170px; }
/* Centred: the cards are short and sit in a symmetric grid, so centring the
   title and blurb reads as deliberate rather than ragged. align-items does the
   work because .card-body is a flex column — text-align alone would not move
   the block itself. */
.card-body { padding: 20px 20px 24px; display: flex; flex-direction: column; gap: 8px;
             align-items: center; text-align: center; }
.card-title { font-family: 'Marcellus', Georgia, serif; font-size: 21px; }
.card-text { font-size: 14.5px; line-height: 1.55; color: var(--muted); }
.grid--services .card-text { font-size: 15.5px; }

.tile { display: flex; flex-direction: column; gap: 18px; color: var(--text); }
/* Landscape rather than square: the photos are as wide as the row allows,
   and a 4:3 crop keeps the band from growing tall as they widen. */
.tile .media { aspect-ratio: 4 / 3; border-radius: 8px; }
/* Same face and size as a service card's title, so the two bands read as one
   system rather than two designs that happen to share a page. */
.tile-label { font-family: 'Marcellus', Georgia, serif; font-size: 22px; line-height: 1.2;
              text-align: center; }

/* ---- reviews ---- */

/* The band is its own field of colour rather than the flat dark it used to
   be: blue held in the top-left corner, green in the bottom-right, and a
   white core showing through the gap they leave in the middle.
   Three things matter about how this is built. Every bloom steps down through
   five stops instead of one, so it dissolves rather than ending on a visible
   ring. The colours are painted ON TOP of the white core, not under it --
   under it they come out grey, because a white veil desaturates whatever it
   covers. And each is pulled back past its own corner, which is what leaves
   the centre clear for the cards to sit on. */
.section--reviews {
  /* A shallow band. The colour field is doing the work of separating this
     section from the ones around it, so the usual 72px of air above and below
     only pushed the gradient's edges away from the content it frames. The
     heading sits 20px from the top edge and the Google button 15px from the
     bottom -- the heading is large type and wants the extra air above it. */
  padding: 20px 0 15px;
  background:
    radial-gradient(64% 120% at -4% 8%,
      rgba(48,126,212,.96) 0%, rgba(48,126,212,.67) 28%, rgba(48,126,212,.40) 50%,
      rgba(48,126,212,.21) 68%, rgba(48,126,212,.08) 84%, rgba(48,126,212,0) 100%),
    radial-gradient(64% 120% at 104% 92%,
      rgba(54,170,118,.96) 0%, rgba(54,170,118,.67) 28%, rgba(54,170,118,.40) 50%,
      rgba(54,170,118,.21) 68%, rgba(54,170,118,.08) 84%, rgba(54,170,118,0) 100%),
    radial-gradient(95% 165% at 50% 50%,
      rgba(255,254,251,1) 0%, rgba(255,254,251,.70) 28%, rgba(255,254,251,.42) 50%,
      rgba(255,254,251,.22) 68%, rgba(255,254,251,.08) 84%, rgba(255,254,251,0) 100%),
    #0f0d0b;
}
/* The band is light where the type sits, so the heading comes back to the
   page's own colour instead of the cream it wore on the dark version. */
.section--reviews .section-title { color: var(--text); }
.section--reviews .reviews-empty { color: var(--muted-strong); }


/* The slider. The track is an ordinary scroll container with snap points, so
   touch, trackpad, keyboard and the arrow buttons all drive the same thing and
   it still works if the script never loads. The buttons sit in their own grid
   columns rather than over the cards, so nothing is ever covered. */
.reviews-slider { display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
                  align-items: center; gap: 14px; }
.reviews-track {
  display: flex; gap: 24px; overflow-x: auto; scroll-snap-type: x mandatory;
  scroll-behavior: smooth; scrollbar-width: none; padding-bottom: 2px;
}
.reviews-track::-webkit-scrollbar { display: none; }
/* Three across on a wide screen, and the basis is what the arrows step by. */
.reviews-track > .review { flex: 0 0 calc((100% - 48px) / 3); scroll-snap-align: start; }
@media (max-width: 980px) { .reviews-track > .review { flex-basis: calc((100% - 24px) / 2); } }

.slider-nav {
  width: 42px; height: 42px; border-radius: 50%; flex: none;
  background: transparent; color: var(--on-dark);
  border: 1px solid #5a4c39; cursor: pointer;
  font-size: 22px; line-height: 1; display: flex; align-items: center;
  justify-content: center; padding: 0 0 2px;
  transition: border-color .15s ease, color .15s ease, opacity .15s ease;
}
.slider-nav:hover { border-color: var(--gold); color: var(--gold); }
/* Dimmed rather than removed at the ends: a control that disappears makes the
   row jump sideways and the cards resize. */
.slider-nav[disabled] { opacity: .3; cursor: default; }
.slider-nav[disabled]:hover { border-color: #5a4c39; color: var(--on-dark); }

/* Landscape cards of one fixed shape. Reviews run from one line to twenty,
   and letting each card size itself gives a ragged wall; a fixed 4:3 makes the
   band read as a set. Grid rather than flex so the meta line is pinned to the
   last row whatever the text does above it. */
/* The card's own colours, set here rather than inherited, so a card looks the
   same wherever it is rendered. On the reviews band they are overridden just
   below: against a light field the warm off-white stops reading as a card, so
   there they go pure white and lean on a deeper shadow instead. */
.review {
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 20px; overflow: hidden;
  /* So the text below can ask how wide its own card is. */
  container-type: inline-size;
  display: grid; grid-template-rows: auto 1fr auto; gap: 10px;
  aspect-ratio: 4 / 3;
}
.review-stars { color: var(--gold-bright); font-size: 17.5px; letter-spacing: 2px; }
/* Cut to whole lines with an ellipsis, not clipped mid-letter by the card's
   overflow. Seven lines is what fits the 4:3 box from a phone width upwards. */
/* How many lines fit is decided by the card, not by a guess. A 4:3 box gets
   shorter as it gets narrower, so one fixed number either overflows at small
   sizes or wastes space at large ones -- and the arrows alone took enough
   width off the cards to break a count that had fitted a moment earlier.
   Container queries let each card answer for itself.

   The arithmetic, for anyone changing these: a container query measures the
   CONTENT box, so the width it reports is already 40px less than the card.
   Height is 0.75 * (that + 40), less the same 40px of padding, less about 59px
   for the stars, the meta line and two gaps -- so roughly 0.75w - 69 is left
   for text, divided by the 25.6px line height. Change the font size and these
   thresholds have to move with it. */
.review-text {
  font-size: 16px; line-height: 1.6; color: var(--text); min-height: 0;
  display: -webkit-box; -webkit-box-orient: vertical;
  overflow: hidden;
  /* The clamp puts an ellipsis on the last line but does not stop later lines
     being laid out when the box is taller -- which it is, the row being 1fr.
     The matching max-height is what actually hides them. */
  -webkit-line-clamp: 3; max-height: calc(1.6em * 3);
}
@container (min-width: 235px) { .review-text { -webkit-line-clamp: 4; max-height: calc(1.6em * 4); } }
@container (min-width: 269px) { .review-text { -webkit-line-clamp: 5; max-height: calc(1.6em * 5); } }
@container (min-width: 303px) { .review-text { -webkit-line-clamp: 6; max-height: calc(1.6em * 6); } }
@container (min-width: 337px) { .review-text { -webkit-line-clamp: 7; max-height: calc(1.6em * 7); } }
.review-meta { display: flex; justify-content: space-between; align-items: end; gap: 8px; font-size: 14.5px; }
.review-name { font-weight: 700; color: var(--text); }
/* Grey rather than the old near-black-background tint, which all but vanished
   once the card went white. */
.review-date { color: var(--muted); }
/* Where we have the source link, the author's name carries it -- Google's
   terms want the review reachable at its origin. The global link colour is a
   mid brown that all but vanishes on this dark band, so inherit instead. */
.review-name a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.review-name a:hover { color: var(--accent); }
.reviews-empty { font-size: 15px; text-align: center; }

/* Against the pale centre of the band, a warm off-white card on warm off-white
   has nothing to separate it, so the card goes white and the shadow does the
   lifting. The arrows need the same treatment: they were drawn as pale outlines
   for a dark background. */
.section--reviews .review { background: #fff; box-shadow: 0 16px 40px rgba(20,30,40,.18); }
.section--reviews .slider-nav { background: rgba(255,255,255,.92); color: var(--text);
                                border-color: rgba(43,37,33,.14); }
.section--reviews .slider-nav:hover { border-color: var(--accent); color: var(--accent); }

/* ---- partners ---- */

/* A strip that advances one logo at a time and rests between moves. The
   motion is driven by a script rather than a CSS animation, because a
   keyframe set that holds between steps would need as many stops as there are
   logos -- a different set per contractor. */
.partners { overflow: hidden;
            -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
            mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent); }
.partners-track { display: flex; width: max-content; gap: 48px; align-items: center;
                  will-change: transform; }
/* Movement someone did not ask for is the kind that causes trouble, so for
   anyone who has asked their system for less of it the strip simply sits
   still, wrapped as it was before. */
@media (prefers-reduced-motion: reduce) {
  .partners-track { transform: none !important; width: auto; flex-wrap: wrap;
                    justify-content: center; gap: 20px; }
}
.partner { width: 280px; height: 120px; flex: none; }
/* A linked logo still has to be a 280x120 box, which an inline anchor is not. */
a.partner--link { display: block; border-radius: 6px; }
a.partner--link:focus-visible { outline: 2px solid var(--gold); outline-offset: 4px; }
a.partner--link:focus-visible .media img { filter: none; opacity: 1; }
/* Grey at rest so a row of competing brand colours does not shout over the
   page, full colour under the cursor -- the strip keeps stepping either way,
   so this is a highlight rather than a way to hold a logo still. */
.partner .media img { filter: grayscale(1); opacity: .72;
                      transition: filter .25s ease, opacity .25s ease; }
.partner:hover .media img { filter: none; opacity: 1; }
.partner .media { width: 100%; height: 100%; border-radius: 6px; background: transparent; }

/* Below about 660px the strip is too narrow for two logos and their gap, so
   it used to show one logo pinned left with a slice of the next beside it.
   Here each logo takes the whole strip instead: one at a time, centred, and
   the step the script measures becomes exactly one screen. The edge fade goes
   too -- with a logo spanning the full width it would dim its own ends. */
@media (max-width: 660px) {
  .partners { -webkit-mask-image: none; mask-image: none; }
  .partners-track { gap: 0; }
  .partner { width: calc(100vw - 48px); height: 110px; }
}

/* ---- awards and licences ---- */

/* A centred row of badges rather than a grid: there are usually only a few,
   and a grid would strand the last one alone on the left of a second row. */
/* A shallower band than the rest: badges are small, self-contained marks and
   do not need a section's worth of air around them. The cut is split between
   the outer padding and the gap under the heading -- taking it all from the
   padding would leave the heading almost touching the divider above it. */
.section--awards { padding: 24px 0; }
.section--awards .section-head { margin-bottom: 23px; }
.awards { display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
          gap: 40px 64px; }
.award { width: 187px; height: 187px; flex: none; }
.award .media { width: 100%; height: 100%; background: transparent; }

/* ---- cta ---- */

.cta-band {
  background: var(--bg-alt);
  padding: 48px 0;
}
.cta-band .container { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 20px; }
.cta-title { font-family: 'Marcellus', Georgia, serif; font-size: 26px; margin-bottom: 6px; }
.cta-sub { font-size: 15px; color: var(--muted); }
/* As an item inside a service page this band has no band before it to
   draw the site-wide divider, so it carries its own -- same 1px rule,
   same width, in place of the band's top padding. */
.cta-band--service { padding-top: 0; }
.cta-band--service::before { content: ""; display: block; height: 1px;
  background: var(--border-2); width: min(100% - 48px, 1132px); margin: 0 auto 48px; }

.cta-box {
  background: var(--bg); border: 1px solid var(--border-2); border-radius: var(--radius);
  padding: 28px; margin: 32px 0; display: flex; flex-wrap: wrap; gap: 16px;
  align-items: center; justify-content: space-between;
}

/* ---- detail blocks (service / material / project / story) ---- */

.detail { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: center; padding: 56px 0; }
.detail .media { aspect-ratio: 4 / 3; border-radius: var(--radius); }
/* ---- pros and cons ---- */

/* Spans both columns, so each side gets half the page rather than half the
   text column. A coloured bar rather than a filled panel: the page is already
   a field of cream blocks, and two more tinted rectangles made the entry look
   busier than it reads. Green and terracotta are opposite each other on the
   wheel but held well down in saturation, so they mark a verdict rather than
   bolting a traffic light onto a cream page.
   The colour is never the only signal -- the word Pros or Cons is set above
   every one of them, for readers who cannot tell the two hues apart. */
.verdict { grid-column: 1 / -1; display: flex; gap: 40px; margin-top: 4px; }
.verdict-item { flex: 1 1 0; padding: 2px 0 2px 16px; }
.verdict-head { font-weight: 700; font-size: 13px; letter-spacing: .08em;
                text-transform: uppercase; }
.verdict-item p { margin: 8px 0 0; font-size: 15px; line-height: 1.6; color: var(--text); }
.verdict-item--pro { border-left: 3px solid #4a7a54; }
.verdict-item--pro .verdict-head { color: #2f5137; }
.verdict-item--con { border-left: 3px solid #aa4a34; }
.verdict-item--con .verdict-head { color: #7a3522; }
.detail h2, .detail h3 { font-size: clamp(24px, 3vw, 32px); margin-bottom: 12px; }
.detail-text { font-size: 16px; line-height: 1.65; color: var(--muted); }
.detail-meta { font-size: 13.5px; color: var(--muted); margin-top: 10px; }
.detail-list { list-style: none; margin: 20px 0 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.detail-list li { border-left: 2px solid var(--gold); padding-left: 14px; }
.detail-list b { font-family: 'Marcellus', Georgia, serif; font-weight: 400; font-size: 17px; display: block; margin-bottom: 3px; }
.detail-list span { font-size: 14.5px; line-height: 1.55; color: var(--muted); }
.traits { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; list-style: none; padding: 0; }
.traits li { border: 1px solid var(--border-2); border-radius: 999px; padding: 6px 14px; font-size: 13.5px; }

.tag { display: inline-block; background: var(--bg-alt); border: 1px solid var(--border); border-radius: 999px; padding: 4px 12px; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--accent); margin-bottom: 10px; }

/* ---- portfolio: cover grid + lightbox ---- */

/* One cover per project, the title underneath. The whole card is the button,
   so there is no small target to hunt for. */
.pcard { position: relative; }
.pcard-btn { display: block; width: 100%; padding: 0; border: 0; background: none;
             cursor: pointer; text-align: center; color: inherit; font: inherit; }
/* No background of its own, so a project with no cover yet still shows the
   standard placeholder hatching rather than a blank rectangle. */
.pcard-cover { display: block; aspect-ratio: 4 / 3; border-radius: var(--radius);
               overflow: hidden; }
.pcard-cover img { width: 100%; height: 100%; object-fit: cover; display: block;
                   transition: transform .35s ease; }
.pcard-btn:hover .pcard-cover img { transform: scale(1.04); }
.pcard-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: var(--radius); }
.pcard-tag { display: block; margin-top: 12px; font-size: 12px; font-weight: 700;
             letter-spacing: .06em; text-transform: uppercase; color: var(--accent); }
.pcard-label { display: block; margin-top: 12px; font-family: 'Marcellus', Georgia, serif;
               font-size: 21px; color: var(--text); }
.pcard-tag + .pcard-label { margin-top: 4px; }

/* The lightbox. Built by the script on first open and reused after that, so a
   page with no portfolio on it never carries one. */
.lightbox { position: fixed; inset: 0; z-index: 90; display: flex; flex-direction: column;
            justify-content: center; background: rgba(16, 14, 12, .94); }
.lightbox[hidden] { display: none; }
/* Photo and caption are centred together as one block, so the words stay with
   the picture instead of drifting to the bottom of the screen. */
.lightbox-stage { flex: 0 1 auto; display: flex; align-items: center; justify-content: center;
                  padding: 56px 72px 0; min-height: 0; }
.lightbox-stage img { max-width: 100%; max-height: 76vh; object-fit: contain;
                      border-radius: 6px; }
.lightbox-foot { flex: 0 0 auto; padding: 18px 24px 8px; text-align: center; color: var(--on-dark); }
.lightbox-title { font-family: 'Marcellus', Georgia, serif; font-size: 22px; }
.lightbox-text { font-size: 14.5px; line-height: 1.6; color: var(--on-dark-muted);
                 max-width: 68ch; margin: 6px auto 0; }
.lightbox-meta { font-size: 13px; color: var(--on-dark-muted); margin-top: 6px; }
.lightbox-count { font-size: 13px; letter-spacing: .08em; color: var(--on-dark-muted); margin-top: 10px; }
.lightbox-btn { position: absolute; display: flex; align-items: center; justify-content: center;
                width: 52px; height: 52px; border-radius: 999px; border: 0; cursor: pointer;
                background: rgba(250, 248, 243, .92); color: #1b1714; }
.lightbox-btn:hover { background: #fff; }
.lightbox-btn svg { width: 22px; height: 22px; fill: none; stroke: currentColor;
                    stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.lightbox-prev { left: 18px; top: 50%; transform: translateY(-50%); }
.lightbox-next { right: 18px; top: 50%; transform: translateY(-50%); }
.lightbox-close { right: 18px; top: 18px; width: 44px; height: 44px; }
.lightbox-btn[disabled] { opacity: .35; cursor: default; }
/* Nothing behind the overlay should scroll while it is open. */
body.is-lightbox { overflow: hidden; }

@media (max-width: 720px) {
  .lightbox-stage { padding: 56px 14px 8px; }
  .lightbox-prev { left: 8px; }
  .lightbox-next { right: 8px; }
  .lightbox-btn { width: 44px; height: 44px; }
  .lightbox-text { display: none; }
}

/* ---- founders ---- */

.founder { text-align: center; }
.founder .media { width: 132px; height: 132px; border-radius: 50%; margin: 0 auto 14px; }
.founder-name { font-family: 'Marcellus', Georgia, serif; font-size: 20px; }
.founder-role { font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: var(--accent); font-weight: 700; margin: 4px 0 10px; }
.founder-bio { font-size: 14.5px; line-height: 1.6; color: var(--muted); }

/* ---- blog ---- */

.post-card .media { height: 200px; }
.post-title { font-family: 'Marcellus', Georgia, serif; font-size: 22px; line-height: 1.25; }
.post-excerpt { font-size: 14.5px; line-height: 1.6; color: var(--muted); }
.byline { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--muted); margin-top: 4px; }
.byline .media { width: 32px; height: 32px; border-radius: 50%; flex: 0 0 32px; }

.article { max-width: 720px; margin: 0 auto; padding: 56px 0; }
.article h1 { font-size: clamp(30px, 4vw, 42px); line-height: 1.15; margin-bottom: 16px; }
.article h2 { font-size: 26px; margin: 36px 0 12px; }
.article p { font-size: 17.5px; line-height: 1.75; color: #3a332c; margin: 0 0 20px; }
.article figure { margin: 28px 0; }
.article figure .media { aspect-ratio: 16 / 9; border-radius: var(--radius); }
.article figcaption { font-size: 13.5px; color: var(--muted); margin-top: 8px; text-align: center; }

/* ---- contact ---- */

.contact-grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: 48px; align-items: start; }
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.field label { font-size: 13px; font-weight: 700; letter-spacing: .04em; }
.field input, .field textarea, .field select {
  font-family: inherit; font-size: 15px; padding: 13px 14px;
  border: 1px solid var(--border-2); border-radius: 8px; background: var(--bg); color: var(--text);
}
.field input:focus, .field textarea:focus, .field select:focus { outline: 2px solid var(--gold); outline-offset: 1px; }
.form-note { font-size: 13px; color: var(--muted); margin-top: 10px; }
.form-msg { margin-top: 12px; font-weight: 600; }
.info-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 18px; }
.info-list dt { font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--accent); font-weight: 700; margin-bottom: 4px; }
.info-list dd { margin: 0; font-size: 16px; }

/* ---- responsive ---- */

@media (max-width: 1000px) {
  .site-nav { display: none; }
  .site-nav.is-open {
    display: flex; flex-direction: column; align-items: stretch; gap: 4px;
    position: absolute; left: 0; right: 0; top: 68px;
    background: var(--bg-alt); border-bottom: 1px solid var(--border-2); padding: 8px 24px 20px; font-size: 17px;
  }
  .nav-toggle { display: flex; }
  .site-header { position: relative; }
  .brand { gap: 9px; }
  .brand-mark { height: 30px; max-width: 44px; }
  .brand-name { font-size: 17px; }
}

@media (max-width: 820px) {
  .section { padding: 25px 0; }
  /* The renderer's column count assumes a desktop width; below that, override. */
  .grid--services { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .detail, .contact-grid, .footer-grid { grid-template-columns: 1fr; gap: 28px; }
  /* Side by side needs two columns' worth of width; stacked below that. */
  .verdict { flex-direction: column; }
  .detail { padding: 36px 0; }
  .hero { min-height: 440px; }
  .hero-inner { padding-top: 64px; padding-bottom: 64px; }
  .hero-logo { height: clamp(56px, 18vw, 96px); margin-bottom: 20px; }
  /* Out of the way of the stacked copy on a phone: smaller, and the copy gets
     enough bottom padding that the button can never sit behind it. */
  /* Centred along the bottom edge rather than tucked into the corner: at this
     width the copy above is centred too, so the column reads straight down. */
  .hero-badge { width: 65px; left: 50%; transform: translateX(-50%); }
  .hero-badge-shape { padding: 13px 5px 7px; }
  .hero-badge-num { font-size: 22px; }
  .hero-badge-label { font-size: 9.5px; letter-spacing: .12em; }
  .hero-badge-ribbon { font-size: 8.5px; padding: 4px 3px; margin: 0 -5px; }
  .hero:has(.hero-badge) .hero-inner { padding-bottom: 110px; }
  /* Too narrow for three across: stack them, mark first. */
  .hero-split { flex-direction: column; gap: 14px; }
  .hero-split-text { flex: 0 0 auto; text-align: center; }
  .hero-split-text:first-child, .hero-split-text:last-child { text-align: center; }
  .hero-split-logo { order: -1; height: clamp(96px, 30vw, 170px); }
  .cta-band .container { flex-direction: column; align-items: flex-start; }
}

/* Hero copy is centred for every site. These sit after the base .hero rules
   above so they win on equal specificity. */
.hero-copy { margin-left: auto; margin-right: auto; text-align: center; }
.hero-copy p { margin-left: auto; margin-right: auto; }
.hero-copy .hero-actions { justify-content: center; }

/* One description serves both the Services page and the home card. Clamp it on
   the card so heights stay uniform however much the customer wrote. */
.grid--services .card-text {
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 2; overflow: hidden;
}

/* ---- services dropdown ---- */

.nav-item.has-menu { position: relative; display: flex; align-items: center; }
.nav-parent {
  background: none; border: none; cursor: pointer; padding: 0;
  font: inherit; color: var(--text); display: inline-flex; align-items: center; gap: 6px;
}
.nav-parent.is-active { color: var(--accent); font-weight: 700; }
.nav-parent .caret {
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid currentColor; transition: transform .15s;
}
.nav-item.has-menu.is-open .nav-parent .caret { transform: rotate(180deg); }

.nav-menu {
  display: none; position: absolute; top: 100%; left: 0; min-width: 230px;
  background: var(--bg); border: 1px solid var(--line); border-radius: 10px;
  padding: 8px; box-shadow: 0 18px 40px rgba(38, 32, 25, .14); z-index: 60;
}
/* Hover for pointers, is-open for taps and keyboards.
   NOT :focus-within — a tap leaves focus on the button, so that rule kept the
   menu open and made the second tap look like it did nothing. The button is a
   real <button>, so Enter/Space fire click and toggle is-open anyway. */
.nav-item.has-menu:hover .nav-menu,
.nav-item.has-menu.is-open .nav-menu { display: block; }
.nav-menu a { display: block; padding: 9px 12px; border-radius: 7px; white-space: nowrap; }
.nav-menu a:hover { background: var(--alt); color: var(--accent); }

@media (max-width: 860px) {
  /* Inside the open mobile panel the menu expands inline rather than floating. */
  .nav-item.has-menu { display: block; }
  .nav-parent { padding: 6px 0; }
  .nav-menu {
    position: static; min-width: 0; border: none; box-shadow: none;
    padding: 0 0 0 14px; background: none;
  }
  .nav-item.has-menu:hover .nav-menu { display: none; }
  .nav-item.has-menu.is-open .nav-menu { display: block; }
}

/* ---- service page ---- */

.card--link { display: block; color: inherit; text-decoration: none; }
.card--link:hover { box-shadow: 0 14px 34px rgba(38, 32, 25, .12); transform: translateY(-2px); }

.hero--service { min-height: 380px; }
/* A service cover is often a close-up of pale tile or stone, which the
   standard scrim is too light to hold the type against. Darker here only, so
   the home cover is untouched, and the line under the title goes up to the
   full light cream rather than the muted one. */
.hero--service .hero-scrim { background: rgba(38, 32, 25, .72); }
.hero--service p { color: var(--on-dark); }
/* Full container width -- the same measure as the home page's sections.
   Only the paragraph keeps a reading cap (below); the heading, photos and
   buttons use the whole band. */
.subservice { margin: 0 auto 48px; }
.subservice:last-child { margin-bottom: 0; }
/* The site-wide divider, one between every pair of subservices: same 1px rule
   in the same colour, the width of the container it already sits in. Drawn on
   the one below so the last block never trails a rule into the footer -- and
   a contact band in the list brings its own. */
.subservice + .subservice::before,
.cta-band--service + .section .subservice:first-child::before {
  content: ""; display: block; height: 1px; background: var(--border-2);
  margin: 0 0 48px;
}
/* Centred over the photos below them, and over the buttons that can close
   the block -- everything else in a subservice is centred, so ranged-left
   copy was the only thing breaking the column. */
.subservice h2 { font-size: clamp(26px, calc(3vw + 2px), 34px); margin: 0 0 12px; text-align: center; }
.subservice p { font-size: 17px; line-height: 1.7; color: var(--muted); margin: 0 auto;
                text-align: center; max-width: 70ch; }

/* Words and photos in one two-column grid, the narrower column for the words.
   The copy is placed by hand; the photos flow into whatever is left, so one
   photo lands beside the words and three fill the block: a large one next to
   them and two across underneath. Any other count still lands somewhere sane,
   but one or three is what the shape is for. */
/* Equal columns, so every photo on the page is the same size whatever row it
   lands in. Rows are sized by the photos' own shape rather than stretched to
   match each other -- a tall portrait photo used to drag a whole row out with
   it and leave the one beside it stranded at the top of a tall empty cell. */
.sub-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; }
.sub-copy { grid-area: 1 / 1; display: flex; flex-direction: column; justify-content: center; }
/* Alternating sides down the page: the words swap columns and the first photo
   takes the cell they left, so the rest flow on unchanged. */
.sub-grid--flip .sub-copy { grid-area: 1 / 2; }
.sub-grid--flip .sub-photo:nth-child(2) { grid-area: 1 / 1; }
/* With no photo at all there is no grid, just the centred column of words. */
.sub-copy--solo { max-width: 70ch; margin: 0 auto; }
/* One shape for every photo, so what the contractor uploads -- portrait,
   landscape, a phone snap -- never changes the layout: the box is 4:3 and the
   picture is cropped to fill it. */
.sub-photo { border-radius: 10px; overflow: hidden; aspect-ratio: 4 / 3; }

/* One column on a narrow screen: the hand-placed cells are released first,
   or the words and the first photo would land on top of each other. */
@media (max-width: 860px) {
  .sub-grid, .sub-grid--flip { grid-template-columns: 1fr; gap: 20px; }
  .sub-copy, .sub-grid--flip .sub-copy,
  .sub-grid--flip .sub-photo:nth-child(2) { grid-area: auto; }
  .sub-copy { margin-bottom: 4px; }
}

/* Material tiles on the home page link to their entry on the Materials page. */
.tile--link { display: flex; color: inherit; text-decoration: none; }
.tile--link:hover { transform: translateY(-2px); }
.tile--link:hover .tile-label { color: var(--accent); }
.tile--link .media, .tile--link .tile-label { transition: none; }
.tile--link { transition: transform .15s ease; }

/* The header is sticky, so an anchored material would land underneath it. */
[id^="material-"] { scroll-margin-top: 84px; }

/* Centred action under a home-page band (e.g. "See all our materials"). */
.band-action { margin-top: 36px; text-align: center; }

/* ---- process band ---- */

/* Heading left, steps right, a rule between them -- and a dashed line joining
   each step to the next so the band reads as a sequence rather than a grid of
   unrelated tiles. */
/* --pgap is both the grid gap and the length of every connector, so the dashes
   always exactly span the space between two steps. --pline is the colour of
   every line in the band, kept in one place so the divider and the connectors
   can never drift apart. */
/* --picon is the icon's footprint and --pclear the air left between an icon
   and any line that reaches it. Every connector is positioned from these two,
   so the gap is identical whichever direction the line runs. */
/* --plane is the clear strip either side of the columns that the row-change
   loops swing out into. It is padding on this container, not margin on the
   cells, so a loop passes beside the captions instead of across them -- a
   caption can use its whole column and still never meet a line. */
/* The heading sits above the steps, so the rule that separates the two runs
   across the top of this block rather than down its side. */
.process-steps { --pgap: 48px; --picon: 72px; --pclear: 12px; --plane: 64px;
                 --pline: var(--gold);
                 display: flex; flex-direction: column; gap: var(--pgap);
                 padding: 40px calc(var(--plane) + 12px) 0;
                 border-top: 2px solid var(--pline); }

/* ---- service steps ---- */

/* The steps on a service page are the Process band's route, so they share all
   of its styling. Two differences only: no rule across the top, because there
   is no heading above it to separate from, and a number in place of the empty
   placeholder where a step has no photo. */
/* Clear of whatever follows it -- the captions on the last row run right to
   the bottom of the band, so without this a subservice heading starts almost
   on top of them. */
.process-steps--bare { border-top: 0; padding-top: 0; margin-bottom: 30px; }
.process-steps--bare:last-child { margin-bottom: 0; }
.process-icon--num { display: flex; align-items: center; justify-content: center; }
/* Sized to fill the icon's box, so a number and a photograph carry the same
   weight in a row that mixes them. */
.process-icon--num span { font-family: 'Karla', system-ui, sans-serif; font-weight: 800;
                          font-size: 44px; line-height: 1; color: var(--gold); }

/* A row centres its own steps, which is the whole reason rows are separate
   containers: a last row of one or two sits in the middle of the band instead
   of hanging off the left. row-reverse rather than re-ordering in the markup,
   so the DOM still reads 1, 2, 3 for a screen reader. */
.process-row { display: flex; gap: var(--pgap); justify-content: center; }
.process-row--rev { flex-direction: row-reverse; }

/* Every step is one slot wide whether or not its row is full, so a short row
   lines up under the one above it instead of spreading out. */
.process-step { position: relative; display: flex; flex-direction: column;
                align-items: center; text-align: center; gap: 12px;
                flex: 0 0 calc((100% - (var(--cols, 3) - 1) * var(--pgap)) / var(--cols, 3)); }
/* A fixed box rather than width:auto. The connectors are placed from the
   icon's half-width, so an icon that sized itself to its own artwork would move
   the line's starting point and the clearance would differ icon to icon.
   object-fit keeps each image's proportions inside the box. */
.process-icon { display: block; width: var(--picon); height: 56px; object-fit: contain; }
.process-icon--empty { border: 1px dashed var(--border-2); border-radius: 8px; }
.process-step-title { font-family: 'Marcellus', Georgia, serif; font-size: 19px; line-height: 1.25; }
.process-step-text { font-size: 14.5px; line-height: 1.6; color: var(--muted); }

/* Connectors. Each step draws the single line that reaches the next step in
   order. Inline SVG rather than borders, because a border cannot curve into a
   route; preserveAspectRatio="none" lets one path stretch to whatever width a
   column happens to be, and non-scaling-stroke keeps the dashes an even 2px
   whatever that stretch turns out to be. */
.process-link { position: absolute; overflow: visible; pointer-events: none;
                fill: none; stroke: var(--pline); stroke-width: 2;
                stroke-linecap: round; stroke-dasharray: 7 8; }
.process-link path { vector-effect: non-scaling-stroke; }

/* Every variant states its width outright. An <svg> is a replaced element, so
   pinning both left and right does not stretch it the way it would a div --
   the browser keeps the intrinsic size and quietly drops one of the offsets,
   which left these lines ending in mid-air. */

/* Along a row: starts and ends --pclear clear of each icon, centred on the
   icons' own centre line (28px down, half the icon's 56px height). The width
   is this cell's remainder, plus the gap, plus the next cell's lead-in. */
.process-link--right, .process-link--left {
  top: 8px; height: 40px;
  width: calc(100% + var(--pgap) - var(--picon) - var(--pclear) * 2);
}
.process-link--right { left: calc(50% + var(--picon) / 2 + var(--pclear)); }
.process-link--left  { right: calc(50% + var(--picon) / 2 + var(--pclear)); }

/* Row change: from this icon's edge, out into the lane, and back to the icon
   directly below. The width carries it past the column edge into the lane;
   100% + the gap is the drop from one icon's centre line to the next. */
.process-link--loop-right, .process-link--loop-left {
  top: 28px; height: calc(100% + var(--pgap));
  width: calc(50% - var(--picon) / 2 - var(--pclear) + var(--plane));
}
.process-link--loop-right { left: calc(50% + var(--picon) / 2 + var(--pclear)); }
.process-link--loop-left  { right: calc(50% + var(--picon) / 2 + var(--pclear)); }

/* The sideways reach into a short row below, picking up where the loop lands.
   --dx is how many slots across the next step sits, in halves. */
.process-link--tail-left, .process-link--tail-right {
  top: calc(100% + var(--pgap) + 8px); height: 40px;
  width: calc(var(--dx, 0) * (100% + var(--pgap)));
}
.process-link--tail-left  { right: calc(50% - var(--picon) / 2 - var(--pclear)); }
.process-link--tail-right { left: calc(50% - var(--picon) / 2 - var(--pclear)); }

.process-link--stack-right, .process-link--stack-left { display: none; }

@media (max-width: 820px) {
  /* The lanes stay: the loops still need somewhere to swing. Narrower than on
     desktop, since they are taking width away from the captions now rather
     than from empty margin. */
  .process-steps { --pgap: 56px; --plane: 36px;
                   padding: 32px calc(var(--plane) + 6px) 0; }
  /* One column, so every row collapses to a stack and the sideways routes
     have nowhere to go. */
  .process-row, .process-row--rev { flex-direction: column; gap: var(--pgap); }
  .process-step { flex: 0 0 auto; }
  .process-link--right, .process-link--left,
  .process-link--loop-right, .process-link--loop-left,
  .process-link--tail-left, .process-link--tail-right { display: none; }
  /* Same loop as a desktop row change, from one icon's edge out into the lane
     and back to the next icon's edge, alternating sides down the column. */
  .process-link--stack-right, .process-link--stack-left {
    display: block; top: 28px; height: calc(100% + var(--pgap));
    width: calc(50% - var(--picon) / 2 - var(--pclear) + var(--plane));
  }
  .process-link--stack-right { left: calc(50% + var(--picon) / 2 + var(--pclear)); }
  .process-link--stack-left  { right: calc(50% + var(--picon) / 2 + var(--pclear)); }
}

@media (max-width: 620px) {
  .awards { gap: 28px 36px; }
  .award { width: 143px; height: 143px; }
}

@media (max-width: 620px) {
  /* On a phone the buttons would eat a quarter of the card's width, and a
     swipe is the natural gesture anyway. */
  .reviews-slider { grid-template-columns: minmax(0, 1fr); }
  .slider-nav { display: none; }
  .reviews-track > .review { flex-basis: 100%; }
  .grid--services { grid-template-columns: 1fr; }
  /* One per row: the card is full width, so a wider ratio keeps it from
     becoming a tall block that pushes the next card off screen. */
  .grid--services .card { aspect-ratio: 3 / 2; }
}
