/* ==========================================================================
   GARD ASSETS — base.css

   THE PALETTE IS THE BRIEF
     White paper, black type, one gold. #b49e34 is the gold on white; #cbb64c is
     the same gold opened up so it survives on photographs. Nothing else has a
     hue: every grey here is black at a lower opacity, and every wash laid over
     a photograph is black. That restriction is the client's and it is the point
     — a second colour would make the page look like a brochure rather than a
     letterhead.

   THE SIGNATURE
     A hairline gold rail down the left of the page, filling as the page is
     read, and a short gold tick where each section begins. It is a measure, not
     an ornament: the firm sells precision and continuity, so the page keeps a
     scale running down its edge.

   TYPE
     Two variables and nothing else: --font-display and --font-text. They are
     rewritten by ?f= at runtime, so every rule below has to go through them.
   ========================================================================== */

:root {
  --paper: #ffffff;
  --ink: #0b0b0b;
  --ink-70: rgba(11, 11, 11, 0.68);
  --ink-45: rgba(11, 11, 11, 0.45);
  --gold: #b49e34;          /* on white */
  --gold-lite: #cbb64c;     /* on black and on photographs */
  --rule: rgba(11, 11, 11, 0.14);
  --rule-soft: rgba(11, 11, 11, 0.08);
  /* One step darker than the section hairlines: the enquiry button is drawn in
     it, and at 1px a 14% line simply disappears against white. */
  --rule-mid: rgba(11, 11, 11, 0.3);

  /* The client's pairing, chosen from the six in the showcase. ?f= still swaps
     both of these at runtime, so no rule below may name a face directly. */
  --font-display: 'Cormorant Garamond', 'Times New Roman', serif;
  --font-text: 'Jost', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;

  --gut: clamp(20px, 5vw, 84px);
  --rail-x: clamp(14px, 2.6vw, 40px);
  --head-h: 108px;               /* the client's number */
  --max: 1440px;

  --step: clamp(64px, 9vw, 140px);   /* space between sections */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-text);
  font-size: clamp(15px, 0.55vw + 13.5px, 17px);
  font-weight: 400;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3, .brand-text, .submit, .cue, .nav-toggle-text {
  font-family: var(--font-display);
  font-weight: 400;
}

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

a { color: inherit; }

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

.skip {
  position: absolute;
  left: 12px; top: -60px;
  z-index: 100;
  padding: 10px 16px;
  background: var(--ink);
  color: #fff;
  text-decoration: none;
  transition: top 0.2s var(--ease);
}
.skip:focus { top: 12px; }

.wrap {
  max-width: var(--max);
  margin: 0 auto;
  padding-inline: var(--gut);
}

/* --- placeholders ----------------------------------------------------------
   Loud on purpose. Anything still reading TBD has to be seen from across the
   room, or it is the thing that ships. */
.tbd {
  background: #ffe94a;
  color: #0b0b0b;
  padding: 0.1em 0.4em;
  box-shadow: 0 0 0 2px #0b0b0b;
  font-weight: 500;
  font-family: var(--font-text);
}

/* ==========================================================================
   The rail — the signature
   ========================================================================== */
.rail {
  position: fixed;
  left: var(--rail-x);
  top: 0; bottom: 0;
  width: 1px;
  background: rgba(180, 158, 52, 0.24);
  z-index: 40;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
  pointer-events: none;
}
body.past-hero .rail { opacity: 1; }
.rail-fill {
  display: block;
  width: 1px;
  height: calc(var(--progress, 0) * 100%);
  background: var(--gold);
}
@media (max-width: 1100px) { .rail { display: none; } }

/* ==========================================================================
   Header and menu
   ========================================================================== */
.site-head {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  height: var(--head-h);
  display: flex;
  align-items: center;
  gap: 24px;
  /* The bar itself still spans the window — its background and its hairline have
     to reach both edges — but what stands on it is held to the same measure as
     every section: max 1440 plus the page gutter. Written as padding rather than
     an inner wrapper because the bar is a flex row of two children already, and
     a third element around them would have to be positioned all over again at
     every breakpoint. The percentage resolves against this element's own width,
     which for a fixed bar pinned left and right is the viewport minus the
     scrollbar — the same width .wrap is centred in, so the wordmark lines up
     with the text of the section below it. */
  padding-inline: max(var(--gut), calc((100% - var(--max)) / 2 + var(--gut)));
  color: #fff;
  transition: background 0.35s var(--ease), color 0.35s var(--ease), box-shadow 0.35s var(--ease);
}

/* Desktop, once the page has moved: frosted white over the white page. */
.site-head.is-stuck {
  background: rgba(255, 255, 255, 0.72);
  -webkit-backdrop-filter: blur(16px) saturate(1.6);
  backdrop-filter: blur(16px) saturate(1.6);
  color: var(--ink);
  box-shadow: inset 0 -1px 0 var(--rule);
}
/* The framed first screen already stands on white, so the header starts inked. */
body.v3 .site-head:not(.is-stuck) { color: var(--ink); }
/* The split first screen is white on one side and footage on the other, so the
   bar wears the frosted treatment from the very first pixel rather than at 24px. */
body.v4 .site-head {
  color: var(--ink);
  background: rgba(255, 255, 255, 0.78);
  -webkit-backdrop-filter: blur(16px) saturate(1.6);
  backdrop-filter: blur(16px) saturate(1.6);
  box-shadow: inset 0 -1px 0 var(--rule);
}

/* THE LOCKUP IS ONE RELATIONSHIP, WRITTEN ONCE.
   The client approved the proportions in the foot and asked for them everywhere:
   the hexagon twice the height of the type, and a gap of eight tenths of it. So
   both places set --brand-type — 26px in the bar, 15px at the foot — and the
   mark and the gap are derived from it. Written as three fixed numbers per place
   they would drift the first time one of them is nudged, which is exactly how
   the bar and the foot came to disagree in the first place. */
.brand, .foot-brand {
  /* The ratios are the foot's own, written as the measurements they came from:
     a 30px hexagon and a 12px gap beside 13px capitals. (13, not the 15 this rule
     used to claim — .site-foot p sets 13px and wins on specificity, so 13px is
     what the client looked at and approved. The number is stated here rather than
     quietly corrected, because correcting it would change the thing he liked.) */
  --brand-mark: calc(var(--brand-type) * 30 / 13);
  --brand-gap: calc(var(--brand-type) * 12 / 13);
}
.brand {
  --brand-type: 26px;            /* the client's number */
  display: inline-flex;
  align-items: center;
  gap: var(--brand-gap);
  text-decoration: none;
  margin-right: auto;
}
.brand-text {
  font-size: var(--brand-type);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  white-space: nowrap;
}
/* The client's hexagon, beside the wordmark. It is a picture with its own gold
   gradient rather than a currentColor drawing like .mark below, so it does not
   invert with the bar: the gradient reads on the dark first screen and on the
   frosted white bar alike. */
.brand-mark {
  /* 60px beside the 26px wordmark — the foot's proportion, which the client
     approved and asked to make the site's standard. It supersedes the 72px taken
     from Evoke, and before that the 100px derived from the bar's height: the mark
     now tracks the type it stands beside, not the bar it sits in. */
  width: var(--brand-mark);
  height: var(--brand-mark);
  flex: none;
}

.mark {
  width: 30px; height: 30px;
  flex: none;
  color: var(--gold-lite);
  fill: none;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.site-head.is-stuck .mark, body.v3 .site-head .mark, body.v4 .site-head .mark { color: var(--gold); }
.mark:empty { display: none; }
.mark--hero { width: clamp(40px, 4vw, 58px); height: clamp(40px, 4vw, 58px); }

.site-nav ul {
  display: flex;
  gap: clamp(16px, 2.2vw, 38px);
  margin: 0; padding: 0;
  list-style: none;
}
.site-nav a {
  text-decoration: none;
  /* 16px, matching tridenttrust.com, which the client named as the reference.
     Face and weight are untouched — he asked for size only. The tracking comes
     down from 0.19em to 0.13em with it: at 12px that ratio was 2.3px, at 16px it
     would be 3px, and the menu would grow wider than the change was about.
     0.13em holds the same 2px gap the reference uses. */
  font-size: 16px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  padding-block: 6px;
  position: relative;
  opacity: 0.88;
  transition: opacity 0.2s var(--ease);
}
.site-nav a::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.32s var(--ease);
}
.site-nav a:hover { opacity: 1; }
.site-nav a:hover::after, .site-nav a.is-current::after { transform: scaleX(1); }
.site-nav a.is-current { color: var(--gold-lite); opacity: 1; }
.site-head.is-stuck .site-nav a.is-current,
body.v3 .site-head .site-nav a.is-current,
body.v4 .site-head .site-nav a.is-current { color: var(--gold); }

.nav-toggle {
  display: none;
  align-items: center;
  gap: 10px;
  background: none;
  border: 0;
  color: inherit;
  padding: 8px 0;
  cursor: pointer;
  font: inherit;
}
.nav-toggle-text { font-size: 12px; letter-spacing: 0.19em; text-transform: uppercase; }
.nav-toggle-bars {
  position: relative;
  width: 22px; height: 9px;
}
.nav-toggle-bars::before, .nav-toggle-bars::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: 1px;
  background: currentColor;
  transition: transform 0.3s var(--ease), opacity 0.2s var(--ease);
}
.nav-toggle-bars::before { top: 0; }
.nav-toggle-bars::after { bottom: 0; }
body.nav-open .nav-toggle-bars::before { transform: translateY(4px) rotate(45deg); }
body.nav-open .nav-toggle-bars::after { transform: translateY(-4px) rotate(-45deg); }

/* The bar's own proportions come down on a phone: the 110px bar and the 26px
   wordmark are instructions for a 1440 screen, and at 390px the wordmark alone
   would be most of the width with nowhere for the Menu button to stand. */
@media (max-width: 860px) {
  :root { --head-h: 64px; }
  /* One number moves and the lockup follows it: 17px of type takes a 34px mark
     and a 13.6px gap, the same proportions as everywhere else. */
  .brand { --brand-type: 17px; }
}

/* THE MENU BECOMES A BUTTON BELOW 1200px, not below 860.
   The laid-out menu needs about 1110px to stand beside the wordmark once the
   type is 16px — the size the client asked for — so between 861 and 1110 it ran
   off the right edge of the screen, and on a portrait iPad (1024) the last
   item was cut in half. Widening the breakpoint is the fix that cannot come
   back: shaving the gaps or the tracking to buy those pixels would leave the
   menu one word away from clipping again at the next content change. A tablet
   showing a menu button is ordinary; a menu with CONTACT sliced off is not. */
@media (max-width: 1200px) {
  .nav-toggle { display: inline-flex; }
  /* backdrop-filter is cleared here, not only background and box-shadow, and it
     is the line that keeps the menu's scrim on the screen. A backdrop-filter
     establishes a containing block for fixed-position descendants, and the
     dropdown IS a fixed descendant of this header — so while the frosted state
     is on, the panel's `inset: 0` resolves against a 64px-tall header instead of
     against the viewport, and the scrim covers the bar alone while the scrolled
     page shows through the list. With the menu open the frosting is invisible
     anyway (the background above is already none), so dropping it costs nothing
     and the frosted bar on the closed, scrolled page is untouched.
     This also covers the v3/v4 first screens, whose headers carry the same
     property with no scroll condition at all. */
  body.nav-open .site-head {
    color: #fff;
    background: none;
    box-shadow: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  body.nav-open .site-head .mark { color: var(--gold-lite); }
  /* The panel is a child of the header, so the wordmark and the close control
     have to be lifted above it or they disappear behind the blur. */
  body.nav-open .brand, body.nav-open .nav-toggle { position: relative; z-index: 2; }

  /* The dropdown's backdrop is black — the client asked for it by name. */
  .site-nav {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.72);
    -webkit-backdrop-filter: blur(22px);
    backdrop-filter: blur(22px);
    color: #fff;
    display: grid;
    place-items: center;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.32s var(--ease), visibility 0.32s;
  }
  body.nav-open .site-nav { opacity: 1; visibility: visible; }
  .site-nav ul {
    flex-direction: column;
    align-items: center;
    gap: 6px;
    text-align: center;
  }
  .site-nav a {
    font-family: var(--font-display);
    font-size: clamp(24px, 7vw, 34px);
    letter-spacing: 0.12em;
    padding-block: 10px;
  }
  .site-nav a.is-current { color: var(--gold-lite); }
}

/* ==========================================================================
   The first screen
   ========================================================================== */
.hero {
  position: relative;
  color: #fff;
  isolation: isolate;
}
.hero-media { position: absolute; inset: 0; overflow: hidden; z-index: 0; }
.hero-video {
  width: 100%; height: 100%;
  object-fit: cover;
}
/* Every wash over footage is black. Never anything else. */
.hero-scrim { position: absolute; inset: 0; }
.hero-inner { position: relative; z-index: 1; }

/* MEASURE BEFORE SIZE. The headline is a single sentence of capitals, and the
   client's own mockup sets it wide — one or two lines across most of the
   screen. A large size in a narrow column turns the same sentence into five
   short stacked lines, which is a different picture altogether. So the size is
   held down and every variant is given a measure in pixels rather than in `ch`:
   `ch` is computed from the *text* face at body size, so `22ch` on a container
   whose child is set at 60px display capitals is roughly a third of the column
   it looks like — the headline then overflows its own box to the right instead
   of centring in it. */
.hero-title {
  margin: 0;
  font-size: clamp(28px, 4.4vw, 60px);
  line-height: 1.1;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-wrap: balance;
}
/* The headline is broken by hand in the content, so each line is its own block —
   and balancing is switched off, or the browser would re-break lines that have
   already been decided. */
.hero-title:has(.hero-line) { text-wrap: initial; }
.hero-line { display: block; }
/* The hinge. "with" joins the two halves of the sentence rather than making a
   third statement of its own, so it is set at just over half the size — small
   enough to read as a connective, large enough not to look like a mistake. Its
   own line-height keeps the two halves from drifting apart around it. */
.hero-line--join {
  font-size: 0.54em;
  line-height: 1.5;
  letter-spacing: 0.14em;
}
/* 18px on a desktop, the client's number. Tracked capitals do not survive being
   scaled by the same ratio as the headline, so this one falls gently: ~15px on a
   tablet and 14px on a phone, where it is a line of its own under the title. */
.hero-sub {
  margin: 0;
  font-size: clamp(14px, 0.5vw + 10.8px, 18px);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-lite);
}
.hero-rule {
  display: block;
  width: 64px; height: 1px;
  background: var(--gold-lite);
}
.cue {
  position: absolute;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  text-decoration: none;
  color: inherit;
  opacity: 0.85;
}
.cue-line { width: 34px; height: 1px; background: currentColor; transform-origin: left; }

/* 1 — the client's mockup: even scrim, headline centred, capitals.
   Two rows, not one centred stack with a floating cue: the words take the free
   height and the scroll indicator gets a row of its own, so the subtitle and
   the cue can never arrive at the same pixel however long the copy runs. */
.hero--1 {
  min-height: 100svh;
  display: grid;
  grid-template-rows: 1fr auto;
  justify-items: center;
  align-items: center;
  row-gap: clamp(20px, 3vh, 40px);
  padding: calc(var(--head-h) + 24px) var(--gut) clamp(28px, 5vh, 52px);
}
.hero--1 .hero-scrim { background: linear-gradient(180deg, rgba(0,0,0,0.62), rgba(0,0,0,0.46) 45%, rgba(0,0,0,0.66)); }
.hero--1 .hero-inner {
  display: grid;
  justify-items: center;
  gap: clamp(18px, 2.2vw, 28px);
  text-align: center;
  width: 100%;
  max-width: min(96vw, 1180px);
}
.hero--1 .hero-title { max-width: none; }
.hero--1 .cue { position: static; transform: none; }
.hero--1 .cue-line { display: none; }

/* 2 — footage left open, black only at the foot, headline in the corner. */
/* The foot padding is the cue's room and nothing else's: the indicator lives in
   the bottom corner, so the block above it stops a clear band short of it. */
.hero--2 { min-height: 100svh; display: flex; align-items: flex-end; padding: calc(var(--head-h) + 24px) var(--gut) clamp(96px, 13vh, 132px); }
.hero--2 .hero-scrim { background: linear-gradient(180deg, rgba(0,0,0,0.5) 0 12%, rgba(0,0,0,0.05) 34%, rgba(0,0,0,0.72)); }
.hero--2 .hero-inner { width: 100%; display: grid; gap: clamp(22px, 3vw, 38px); }
.hero--2 .hero-rule--wide { width: 100%; background: rgba(203, 182, 76, 0.55); }
.hero--2 .hero-row { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: clamp(24px, 4vw, 64px); align-items: end; }
.hero--2 .hero-title { max-width: none; }
.hero--2 .hero-aside { display: grid; gap: 18px; justify-items: start; align-content: end; }
.hero--2 .cue { right: var(--gut); bottom: clamp(30px, 4.5vh, 46px); flex-direction: row-reverse; }

/* 3 — the footage held inside a white margin, like a page in a magazine. */
.hero--3 { padding: var(--head-h) 0 0; background: var(--paper); }
.hero-frame {
  position: relative;
  height: calc(100svh - var(--head-h) - var(--gut));
  min-height: 460px;
  margin: 0 var(--gut) var(--gut);
  overflow: hidden;
  color: #fff;
}
.hero--3 .hero-scrim { background: linear-gradient(115deg, rgba(0,0,0,0.72) 0 34%, rgba(0,0,0,0.12) 74%); }
.hero--3 .hero-inner { position: absolute; inset: 0; display: flex; align-items: flex-end; padding: clamp(22px, 3vw, 46px); }
.hero--3 .hero-panel {
  display: grid;
  gap: 20px;
  max-width: min(90%, 560px);
  padding: clamp(24px, 3vw, 40px);
  background: rgba(0, 0, 0, 0.52);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  border-left: 1px solid var(--gold-lite);
}
.hero--3 .hero-title { font-size: clamp(23px, 2.4vw, 34px); }
.hero--3 .cue { right: clamp(22px, 3vw, 46px); bottom: clamp(22px, 3vw, 46px); flex-direction: row-reverse; }

/* 4 — the screen cut in two: white column of black type, footage beside it. */
.hero--4 { background: var(--paper); color: var(--ink); }
/* The white column is given a little more of the screen than half the footage:
   the headline is the reason this variant exists, and it has to have somewhere
   to be wide. */
.hero-split {
  display: grid;
  grid-template-columns: minmax(0, 5.6fr) minmax(0, 6.4fr);
  min-height: 100svh;
}
.hero--4 .hero-card {
  position: relative;
  display: grid;
  align-content: center;
  gap: 26px;
  padding: calc(var(--head-h) + 40px) clamp(24px, 4vw, 72px) 64px;
}
.hero--4 .hero-title { color: var(--ink); font-size: clamp(27px, 3.5vw, 48px); }
.hero--4 .hero-rule { background: var(--gold); width: 88px; }
.hero--4 .hero-sub { color: var(--gold); }
.hero--4 .hero-media { position: relative; inset: auto; min-height: 320px; }
.hero--4 .hero-scrim { background: linear-gradient(90deg, rgba(0,0,0,0.28), rgba(0,0,0,0.05)); }
.hero--4 .cue { position: static; margin-top: 10px; color: var(--ink-45); }

@media (max-width: 860px) {
  .hero-split { grid-template-columns: 1fr; min-height: 0; }
  .hero--4 .hero-media { order: -1; height: 52svh; }
  .hero--4 .hero-card { padding: 44px var(--gut) 56px; }
  .hero--2 .hero-row { grid-template-columns: 1fr; align-items: start; }
  .hero--2 .hero-title { max-width: none; }
  .hero--2 .cue, .hero--3 .cue { display: none; }
  .hero-frame { height: calc(100svh - var(--head-h) - 20px); margin: 0 12px 12px; }
}

/* ==========================================================================
   Sections
   ========================================================================== */
.section, .hero { scroll-margin-top: calc(var(--head-h) - 1px); }
.section { padding-block: var(--step); }
.section--services { padding-block: var(--step) 0; }
.section--services + .section { padding-top: var(--step); }
.section--advantages { padding-bottom: var(--step); }

/* The section heading is now a display line, not an eyebrow: 45px in the
   display face at the client's instruction. The gold tick stays, but it can no
   longer hang off a baseline shared with 45px type — it is lifted against the
   heading's own cap height instead, so the rule sits mid-word rather than under
   the letters. */
.head {
  display: flex;
  align-items: baseline;
  gap: clamp(14px, 1.6vw, 24px);
  margin-bottom: clamp(30px, 3.4vw, 54px);
}
.head .tick {
  flex: none;
  align-self: center;
  width: clamp(28px, 4vw, 64px);
  height: 1px;
  background: var(--gold);
}
/* 45px on a desktop → ~34px on a tablet → 28px on a phone, where 45px capitals
   would take three lines of a 390px screen before the section has begun. The
   face comes from --font-display, so ?f= still swaps it with everything else. */
.head h2 {
  margin: 0;
  font-size: clamp(28px, 1.82vw + 18.8px, 45px);
  line-height: 1.12;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--ink);
}

.prose p { margin: 0 0 1.15em; }
.prose p:last-child { margin-bottom: 0; }

/* --- About: text left, photograph right ---------------------------------- */
/* Both paragraphs are set at the same size at the client's instruction — the
   hierarchy is carried by colour alone now, not by size. The block starts at the
   top of the row rather than centred in it, so its first line sits level with
   the top edge of the photograph beside it. `start` alone leaves the text a
   line's half-leading low, which is visible against a hard photographic edge, so
   the first line is pulled back up by exactly that half-leading. */
.about {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(28px, 5vw, 96px);
  align-items: stretch;
}
.about-copy { align-self: start; }
.about-copy p {
  font-family: var(--font-display);
  font-size: clamp(20px, 1.95vw, 28px);
  line-height: 1.36;
  letter-spacing: 0.005em;
}
.about-copy p:first-child { margin-top: -0.18em; }
.about-copy p + p {
  line-height: 1.36;
  color: var(--ink-70);
  max-width: 54ch;
  margin-top: 0.9em;
}
/* The photograph takes the height of the row rather than a height of its own,
   so it always stands level with the copy beside it instead of stopping short. */
.about-shot { display: grid; }
.about-shot picture { display: block; height: 100%; }
.about-shot img {
  width: 100%;
  height: 100%;
  min-height: clamp(300px, 32vw, 470px);
  object-fit: cover;
}

@media (max-width: 860px) {
  .about { grid-template-columns: 1fr; }
  .about-shot { order: -1; }
}

/* --- Services: two photographs that carry their own copy ------------------
   The client's instruction has two halves that pull opposite ways: the
   photograph edge to edge, the words inside the page measure. So the section
   breaks out of .wrap — the frame, its veil and its photograph reach both edges
   of the window — and the measure is restored INSIDE each frame, by padding the
   article to max 1440 plus the gutter. The copy therefore lines up with the text
   of About and with the wordmark in the header, while nothing about the picture
   is cropped to a container. */
.section--services .wrap { padding-inline: 0; max-width: none; }
.section--services .head { max-width: var(--max); margin-inline: auto; padding-inline: var(--gut); }
.services { display: grid; }
.service {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
  min-height: clamp(560px, 82vh, 780px);
  padding-block: clamp(36px, 6vw, 96px);
  /* The measure, restored inside a full-bleed frame — same expression as the
     header bar, so the two agree on where the page's left edge is. */
  padding-inline: max(var(--gut), calc((100% - var(--max)) / 2 + var(--gut)));
  color: #fff;
}
.service--right { justify-content: flex-end; }
.shot--cover { position: absolute; inset: 0; z-index: 0; }
.shot--cover img { width: 100%; height: 100%; object-fit: cover; }
/* Two black washes, never anything but black: one across the frame, one heavier
   on the side the words sit, so the copy holds on a bright photograph as well
   as a dark one. */
.service::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(90deg, rgba(0,0,0,0.86) 0 38%, rgba(0,0,0,0.3) 76%, rgba(0,0,0,0.2)),
    linear-gradient(180deg, rgba(0,0,0,0.4), rgba(0,0,0,0.62));
}
.service--right::after {
  background:
    linear-gradient(270deg, rgba(0,0,0,0.86) 0 38%, rgba(0,0,0,0.3) 76%, rgba(0,0,0,0.2)),
    linear-gradient(180deg, rgba(0,0,0,0.4), rgba(0,0,0,0.62));
}
.service-body { position: relative; z-index: 2; max-width: 58ch; display: grid; gap: clamp(20px, 2.4vw, 32px); }
.service-body h3 {
  margin: 0;
  font-size: clamp(26px, 3.4vw, 46px);
  line-height: 1.14;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #fff;
}
.lines { margin: 0; padding: 0; list-style: none; display: grid; }
/* 21px on a desktop, the client's number — these are long sentences, so the
   measure above (58ch) is what keeps them from running the width of the screen.
   ~18px on a tablet and ~16px on a phone, where 21px would give three or four
   lines per bullet. */
.lines li {
  padding: 14px 0 14px 26px;
  position: relative;
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  font-size: clamp(15.5px, 0.5vw + 13.8px, 21px);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.9);
}
.lines li::before {
  content: '';
  position: absolute;
  /* the middle of the first line, whatever size that line ends up: the padding
     is fixed, half the line box is not */
  left: 0; top: calc(14px + 0.75em);
  width: 12px; height: 1px;
  background: var(--gold-lite);
}
.lines li:last-child { border-bottom: 1px solid rgba(255, 255, 255, 0.18); }

@media (max-width: 720px) {
  .service { min-height: 0; padding-block: 56px; }
  .service-body h3 { font-size: clamp(23px, 7vw, 30px); }
}

/* --- Sectors: the words set ON the photograph -----------------------------
   The client's instruction for this section was "the same as on the other
   site", and on that site a sector is not a thumbnail with a caption under it:
   it is a photograph with a permanent black veil, the title in gold and the
   list in white sitting at the foot of the frame, and the six tiles butt
   together into one block separated by hairlines. The hover is inverted on
   purpose — the photograph steps back so the words gain contrast, rather than
   the photograph coming forward. What stays ours: the page around it is white,
   the accent is our gold, and every veil is black. */
.tiles {
  margin: 0; padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 2.4vw, 40px) clamp(20px, 2.6vw, 48px);
}
.tiles--sectors { gap: 1px; background: var(--rule); }
.sector {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: clamp(320px, 27vw, 400px);
  padding: clamp(22px, 2.2vw, 34px);
  background: var(--ink);
  color: #fff;
}
.sector .shot--cover img {
  opacity: 0.7;
  filter: saturate(0.85) contrast(1.02);
  transition: opacity 0.55s var(--ease), filter 0.55s var(--ease);
}
.sector::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(0deg, rgba(0,0,0,0.92) 14%, rgba(0,0,0,0.34));
  transition: background 0.55s var(--ease);
}
.sector:hover .shot--cover img { opacity: 0.22; filter: saturate(0.5) contrast(1); }
.sector:hover::after { background: linear-gradient(0deg, rgba(0,0,0,0.96) 12%, rgba(0,0,0,0.8)); }
.sector-body { position: relative; z-index: 2; }
/* 32px on a desktop at the client's instruction. The tiles are three across, so
   a title has roughly 430px of frame to sit in — "Infrastructure" in tracked
   32px capitals is about 300px of that. Two across on a tablet is the same width
   again, so the size barely moves; on a phone the tile is full width and 24px is
   plenty. */
.sector h3 {
  margin: 0 0 14px;
  font-size: clamp(24px, 1vw + 17.6px, 32px);
  line-height: 1.15;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gold-lite);
}
/* 21px, the same number the client gave for the service bullets. */
.lines--tight li {
  padding: 9px 0 9px 22px;
  font-size: clamp(15.5px, 0.5vw + 13.8px, 21px);
}
.lines--tight li::before { top: calc(9px + 0.75em); width: 10px; }

/* --- Advantages: six photographs with the words set on them --------------- */
.advantage {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
  min-height: clamp(280px, 30vw, 400px);
  padding: clamp(22px, 2.2vw, 34px);
  color: #fff;
  overflow: hidden;
}
.advantage::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(0,0,0,0.12) 0 26%, rgba(0,0,0,0.72) 58%, rgba(0,0,0,0.92));
  transition: opacity 0.5s var(--ease);
}
.advantage .shot--cover img {
  filter: grayscale(0.5) contrast(1.05);
  transition: transform 0.8s var(--ease), filter 0.8s var(--ease);
}
.advantage:hover .shot--cover img { filter: grayscale(0) contrast(1); }
.advantage:hover .shot--cover img { transform: scale(1.05); }
.advantage-body { position: relative; z-index: 2; display: grid; gap: 10px; }
/* 32px, the same as a sector title — the client asked for the two sets to match.
   At that size two of the six titles take a second line, and with the block
   pinned to the foot of the tile that alone would put every title in the row at
   a different height. Two lines are therefore reserved for the title exactly as
   three are for the caption below it: both edges then hold across the row. On a
   phone the tiles are stacked one per row, nothing has to line up with anything,
   and the reservation is dropped so it does not read as a gap. */
.advantage h3 {
  margin: 0;
  min-height: 2.3em;
  font-size: clamp(24px, 1vw + 17.6px, 32px);
  line-height: 1.15;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gold-lite);
}
/* The captions run from one line to three, and with the block pinned to the
   foot of the tile that pushed each title to a different height — six tiles
   that ought to read as one set. Reserving three lines for the caption fixes
   both edges at once: the titles line up across the row and the block still
   ends level with the bottom of every frame. */
.advantage p {
  margin: 0;
  font-size: clamp(15.5px, 0.5vw + 13.8px, 21px);
  line-height: 1.45;
  min-height: 4.35em;
  max-width: 30ch;
  color: rgba(255, 255, 255, 0.92);
}

@media (max-width: 980px) { .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) {
  .tiles { grid-template-columns: 1fr; }
  .advantage h3 { min-height: 0; }
  .advantage p { min-height: 0; }
}

/* --- Contact: photograph left, form right --------------------------------- */
.contact {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 72px);
  align-items: stretch;
}
/* Photograph left, words and form right. The copy used to be set in white over
   the foot of the photograph, where it read as an accident rather than as a
   caption; it now sits at the head of the reading column, and the photograph is
   left to be a photograph — full height of the row, no veil, because nothing is
   written on it any more. */
.contact-shot {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  min-height: clamp(360px, 46vw, 660px);
}
.contact-words p {
  font-family: var(--font-display);
  font-size: clamp(18px, 1.5vw, 22px);
  line-height: 1.45;
  max-width: 48ch;
}
.contact-words p + p { color: var(--ink-70); }

.contact-panel { display: grid; align-content: start; gap: clamp(24px, 2.8vw, 38px); }
.form { display: grid; gap: 22px; margin: 0; }
.field { display: grid; gap: 8px; margin: 0; }
.field label {
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-45);
}
.field input, .field textarea {
  font: inherit;
  font-family: var(--font-text);
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--rule);
  padding: 10px 2px;
  border-radius: 0;
  transition: border-color 0.3s var(--ease);
  width: 100%;
}
.field textarea { resize: vertical; min-height: 120px; }
.field input:hover, .field textarea:hover { border-bottom-color: var(--ink-45); }
.field input:focus, .field textarea:focus {
  outline: none;
  border-bottom-color: var(--gold);
  box-shadow: 0 1px 0 0 var(--gold);
}
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
/* The captcha, when a site key was given at build time. Left-aligned with the
   button under it rather than stretched across the column. */
.cf-turnstile { justify-self: start; }

/* The client's button: white, an outline a step lighter than the near-black it
   used to be filled with, and the word one size up. 14px holds on every screen —
   it is twelve tracked capitals, not a paragraph — so this one does not scale. */
.submit {
  justify-self: start;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule-mid);
  padding: 15px 40px;
  font-size: 14px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.3s var(--ease), color 0.3s var(--ease), border-color 0.3s var(--ease);
}
.submit:hover { background: var(--paper); border-color: var(--gold); color: var(--gold); }
/* The standing promise under the button, and then the answer from the server in
   the same place. Red is the one exception to "no second hue on this page": a
   refusal that is merely grey is read as decoration and the visitor leaves
   believing the enquiry went. */
.form-note {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-45);
}
.form-note:focus { outline: none; }
.form-note:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
.form-note.is-live { color: var(--gold); }
.form-note.is-error { color: #b3352e; }

.details { margin: 0; padding: 0; list-style: none; display: grid; gap: 0; }
.details li {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: 16px;
  align-items: baseline;
  padding: 16px 0;
  border-top: 1px solid var(--rule-soft);
}
.details li span {
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-45);
}

@media (max-width: 860px) {
  .contact { grid-template-columns: 1fr; }
  .contact-shot { min-height: 300px; }
}

/* ==========================================================================
   Footer
   ========================================================================== */
.site-foot {
  border-top: 1px solid var(--rule);
  padding-block: clamp(34px, 4vw, 56px);
}
.site-foot .wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 12px clamp(20px, 4vw, 60px);
}
.site-foot p { margin: 0; font-size: 13px; color: var(--ink-45); }
.foot-brand {
  /* The lockup the client approved, and the one the header now copies: 15px of
     type, a 30px mark, a 12px gap — expressed as the shared relationship rather
     than as three numbers, so the two places cannot drift apart again. Flex, so
     the hexagon centres on the capitals instead of standing on their baseline. */
  --brand-type: 13px;
  display: flex;
  align-items: center;
  gap: var(--brand-gap);
  font-family: var(--font-display);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink) !important;
}
.foot-mark { width: var(--brand-mark); height: var(--brand-mark); flex: none; }
/* The wordmark and its hexagon sit at the right end of the line, the two lines of
   text at the left — the client's arrangement. The auto margin is on the brand
   rather than on the legal line, which is what puts every text item on the left
   and pushes the mark alone to the far edge. */
.foot-brand { margin-left: auto !important; }
/* On a phone the row has nowhere to spread, and left-and-right becomes two items
   crowding one line with the mark stranded in the middle of it. Below 560 the
   foot stacks instead, each line starting at the same left edge as the page. */
@media (max-width: 560px) {
  .site-foot .wrap { flex-direction: column; align-items: flex-start; gap: 14px; }
  .foot-brand { margin-left: 0 !important; }
}

/* ==========================================================================
   Motion — entrances are a courtesy, never a requirement
   ========================================================================== */
/* The class is added by site.js, never by the markup: with the script blocked
   the page is simply visible, which is the only acceptable failure mode. */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
}
.reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1 !important; transform: none !important; }
  .sector:hover .sector-shot img,
  .advantage:hover .shot--cover img { transform: none; }
}
