@import url('https://fonts.googleapis.com/css2?family=Tinos:ital,wght@0,400;0,700;1,400&family=Open+Sans:wght@300;400;500;600;700&display=swap');

/* ============ TOKENS ============ */
:root {
  /* Brand — the client's stated values, verified by sampling their logo file
     (LOGO COLORS.docx, 2026-08-16). Red 234/3/3, grey 128/128/128.
     The previous palette was derived from a screenshot of the RETIRED logo
     (#D30145) and has been replaced wholesale. */
  --brand:       #EA0303;   /* the logo red — 4.65:1 on white, passes AA both ways */
  --accent:      #EA0303;   /* same value: white text on it also clears 4.5:1 */
  --accent-hi:   #FF5C4A;   /* lifted, for accent text on dark grounds — 5.36:1 on --ink */
  --accent-ink:  #C40202;   /* small accent text on light — 6.26:1 */
  --brand-wash:  #FDECEA;   /* palest tint */
  --brand-grey:  #808080;   /* the client's stated grey — 3.95:1, NOT for body text.
                               Rules, borders, captions and large display only. */

  --ink:        #1E2022;    /* sampled from the logo wordmark */
  --navy:       #26292C;
  --navy-2:     #2F3337;
  --navy-3:     #3B4045;
  --slate:      #3C4247;
  --muted:      #565C61;
  --line:       #D9DCDF;
  --line-dark:  rgba(255,255,255,.13);
  --paper:      #FFFFFF;
  --paper-2:    #FAFAFB;
  --paper-3:    #F1F2F4;

  --r-sm: 4px;
  --r:    10px;
  --r-lg: 16px;

  --shadow-sm: 0 1px 2px rgba(30,32,34,.06), 0 2px 8px rgba(30,32,34,.04);
  --shadow:    0 4px 12px rgba(30,32,34,.07), 0 12px 32px rgba(30,32,34,.07);
  --shadow-lg: 0 12px 28px rgba(30,32,34,.10), 0 32px 64px rgba(30,32,34,.12);

  /* 2026-09-30: the page read as compressed — 56px of gutter inside a 1240px
     container left ~155px of dead margin each side on a 1440 screen. The gutter
     is down to 40px at desktop and the container is wider, which gives the
     content about 150px more width without touching any section's own layout.
     Phone gutters are unchanged at 20px; the clamp floor holds them. */
  /* Halved again 2026-09-30. Two things read as side padding and both had to
     come down: the gutter inside the container, and the dead margin left when
     the container cap is narrower than the screen. Edge-of-window to content is
     now 37px on a 1440 display (was 80, and 156 before that), 33px at 1280 (was
     76) and 122px at 1728 (was 224) — half, at every width that matters. The
     gutter carries the reduction on a laptop and the wider cap carries it on a
     large display. Phone gutters stay put at 18px. */
  --wrap: 1560px;
  --gutter: clamp(18px, 2.6vw, 38px);
  --nav-h: 78px;

  --ease: cubic-bezier(.22,.61,.36,1);
}

/* ============ RESET ============ */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: calc(var(--nav-h) + 24px); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  font-size: 17px;
  line-height: 1.65;
  color: var(--slate);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg, video { display: block; max-width: 100%; }
img { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }

h1, h2, h3 {
  font-family: 'Tinos', 'Times New Roman', Georgia, serif;
  color: var(--ink);
  font-weight: 600;
  line-height: 1.14;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
h4, h5 { color: var(--ink); font-weight: 700; line-height: 1.2; letter-spacing: -0.01em; }
h1 { font-size: clamp(2.5rem, 5.8vw, 4.3rem); font-weight: 700; letter-spacing: -0.02em; line-height: 1.08; }
h2 { font-size: clamp(1.95rem, 4vw, 2.95rem); }
h3 { font-size: clamp(1.2rem, 2.1vw, 1.5rem); }
h4 { font-size: 1.05rem; }
p  { text-wrap: pretty; }

::selection { background: var(--brand); color: #fff; }

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  border-radius: 3px;
}

.skip {
  position: absolute; left: 12px; top: -60px; z-index: 200;
  background: var(--ink); color: #fff; padding: 12px 20px; border-radius: var(--r-sm);
  transition: top .2s var(--ease);
}
.skip:focus { top: 12px; }

/* ============ LAYOUT ============ */
.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }
.section { padding-block: clamp(72px, 10vw, 132px); }
.section--tight { padding-block: clamp(56px, 7vw, 88px); }
.section--dark { background: var(--navy); color: rgba(255,255,255,.72); }
.section--dark h1, .section--dark h2, .section--dark h3, .section--dark h4 { color: #fff; }
.section--alt { background: var(--paper-2); }

.grid { display: grid; gap: clamp(20px, 2.4vw, 32px); }
.cols-2 { grid-template-columns: repeat(2, 1fr); }
.cols-3 { grid-template-columns: repeat(3, 1fr); }
.cols-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1000px) { .cols-3, .cols-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 700px)  { .cols-2, .cols-3, .cols-4 { grid-template-columns: 1fr; } }

/* ============ TYPE HELPERS ============ */
.eyebrow {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: .74rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
  color: var(--accent-ink); margin-bottom: 18px;
}
.eyebrow::before { content: ''; width: 28px; height: 2px; background: var(--accent); }
.section--dark .eyebrow { color: var(--accent-hi); }

.lede { font-size: clamp(1.06rem, 1.5vw, 1.24rem); line-height: 1.6; color: var(--slate); }
.section--dark .lede { color: rgba(255,255,255,.78); }
.measure { max-width: 62ch; }
.measure-sm { max-width: 48ch; }
.center { text-align: center; margin-inline: auto; }

.section-head { max-width: 720px; margin-bottom: clamp(40px, 5vw, 64px); }
.section-head.center { text-align: center; }
.section-head.center .eyebrow { justify-content: center; }

/* ============ BUTTONS ============ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  padding: 15px 28px; border-radius: 999px; border: 1.5px solid transparent;
  font-weight: 600; font-size: .95rem; letter-spacing: -0.01em;
  cursor: pointer; text-align: center;
  transition: background-color .22s var(--ease), color .22s var(--ease),
              border-color .22s var(--ease), box-shadow .22s var(--ease);
  min-height: 48px;
}
.btn svg { width: 17px; height: 17px; flex: none; }
.btn--primary { background: var(--accent); color: #fff; }
.btn--primary:hover { background: var(--accent-ink); box-shadow: 0 8px 24px rgba(234, 3, 3,.34); }
.btn--dark { background: var(--ink); color: #fff; }
.btn--dark:hover { background: var(--navy-3); box-shadow: var(--shadow); }
.btn--ghost { border-color: var(--line); color: var(--ink); background: transparent; }
.btn--ghost:hover { border-color: var(--ink); background: var(--ink); color: #fff; }
.btn--onDark { border-color: rgba(255,255,255,.28); color: #fff; }
.btn--onDark:hover { background: #fff; color: var(--ink); border-color: #fff; }
.btn--sm { padding: 11px 20px; min-height: 42px; font-size: .875rem; }

.link-arrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-weight: 600; color: var(--ink); font-size: .95rem;
  border-bottom: 1.5px solid var(--accent); padding-bottom: 3px;
  transition: gap .22s var(--ease), color .22s var(--ease);
}
.link-arrow:hover { gap: 14px; color: var(--accent-ink); }
.section--dark .link-arrow { color: #fff; }
.section--dark .link-arrow:hover { color: var(--accent-hi); }
.link-arrow svg { width: 15px; height: 15px; }

/* ============ HEADER ============ */
.header {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  height: var(--nav-h);
  display: flex; align-items: center;
  background: transparent;
  transition: background-color .3s var(--ease), box-shadow .3s var(--ease), height .3s var(--ease);
}
/* The header sits transparent over the hero and gains a ground on scroll.
   That ground is DARK, not light. Scrolling starts at 40px — well inside a
   full-height hero image — so a white bar dropped in over dark photography,
   flipping the logo, nav, phone and burger from white to ink at the same
   moment. One dark ground keeps every element in a single colour state for
   the whole scroll, and lets the logo stay the knockout at all times. */
.header::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: rgba(30,32,34,.90);
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  border-bottom: 1px solid rgba(255,255,255,.10);
  opacity: 0; transition: opacity .3s var(--ease);
}
.header.is-stuck { height: 68px; }
.header.is-stuck::after { opacity: 1; }

.header .wrap { display: flex; align-items: center; gap: 28px; width: 100%; }

/* The client's own logo (LOGO COLORS.docx, 2026-08-16), replacing the CSS
   reconstruction drawn from the low-res screenshot on the live site.
   Regenerated by make-logo.py — see that file for why naive background removal
   left the wordmark washed out. The header ground is always dark, so the
   knockout export is the only one the chrome needs. */
.brand { display: flex; align-items: center; margin-right: auto; flex: none; }
.brand__logo {
  display: block; flex: none;
  height: 42px; width: auto;
  transition: height .3s var(--ease);
}
.header.is-stuck .brand__logo { height: 36px; }

.nav { display: flex; align-items: center; gap: 2px; flex: none; flex-wrap: nowrap; }
.nav a {
  padding: 10px 12px; border-radius: 999px; font-size: .92rem; font-weight: 500;
  color: rgba(255,255,255,.86); white-space: nowrap;
  transition: color .2s var(--ease), background-color .2s var(--ease);
}
.nav a:hover { color: #fff; background: rgba(255,255,255,.12); }
.nav a[aria-current="page"] { color: var(--accent-hi); font-weight: 600; }

.header__cta { display: flex; align-items: center; gap: 12px; flex: none; }
/* Nothing in the header row may shrink. Left to itself flexbox took the width out
   of the two things that could give — the logo squashed horizontally (the wordmark
   lost letters) and "Request a Proposal" wrapped to two and three lines, turning
   the pill into an oval. The row now keeps its true width and the breakpoints
   below decide what to drop, rather than mangling what stays. */
.header__cta .btn { white-space: nowrap; flex: none; }
.header__tel {
  display: inline-flex; align-items: center; gap: 8px;
  font-weight: 600; font-size: .92rem; color: #fff; padding: 8px 4px;
  white-space: nowrap; flex: none;
  transition: color .2s var(--ease);
}
.header__tel svg { width: 16px; height: 16px; }
.header__tel:hover { color: var(--accent-hi); }

.burger {
  display: none; width: 46px; height: 46px; border: 0; background: transparent;
  cursor: pointer; border-radius: 10px; place-items: center;
}
.burger span { display: block; width: 22px; height: 2px; background: #fff; border-radius: 2px; position: relative; transition: background .2s; }
.burger span::before, .burger span::after { content: ''; position: absolute; left: 0; width: 22px; height: 2px; background: inherit; border-radius: 2px; transition: transform .25s var(--ease); }
.burger span::before { top: -7px; } .burger span::after { top: 7px; }
.burger[aria-expanded="true"] span { background: transparent; }
.burger[aria-expanded="true"] span::before { transform: translateY(7px) rotate(45deg); background: var(--ink); }
.burger[aria-expanded="true"] span::after  { transform: translateY(-7px) rotate(-45deg); background: var(--ink); }

/* The phone number is the first thing to go: it is repeated in the mobile menu
   and in the footer, and dropping it buys the nav ~140px. */
@media (max-width: 1320px) {
  .header__tel { display: none; }
}

/* Below this the seven nav items, the logo and the pill genuinely do not fit on
   one line, so the whole desktop row gives way to the burger menu. Was 1040px,
   which was already ~200px too optimistic for six items — at 1050 the pill was
   breaking onto three lines. */
@media (max-width: 1200px) {
  .nav { display: none; }
  .burger { display: grid; }
  .header__cta .btn { display: none; }
}

.mobile-nav {
  position: fixed; inset: var(--nav-h) 0 0 0; z-index: 99;
  background: var(--paper); padding: 28px var(--gutter) 40px;
  display: flex; flex-direction: column; gap: 4px;
  transform: translateY(-12px); opacity: 0; pointer-events: none;
  transition: opacity .25s var(--ease), transform .25s var(--ease);
  overflow-y: auto;
}
.mobile-nav.is-open { opacity: 1; transform: none; pointer-events: auto; }
.mobile-nav a {
  padding: 16px 4px; font-size: 1.28rem; font-weight: 600; color: var(--ink);
  border-bottom: 1px solid var(--line); letter-spacing: -0.02em;
}
.mobile-nav .btn { margin-top: 24px; }

/* ============ HERO ============ */
.hero { position: relative; min-height: 92vh; display: flex; align-items: flex-end; overflow: hidden; background: var(--ink); }
.hero--short { min-height: 62vh; }
.hero__bg { position: absolute; inset: 0; }
.hero__bg img { width: 100%; height: 100%; object-fit: cover; opacity: .55; filter: saturate(.75); }
.hero__bg::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(30,32,34,.9) 0%, rgba(30,32,34,.55) 38%, rgba(30,32,34,.95) 100%);
}
.hero__grid {
  position: absolute; inset: 0; pointer-events: none; opacity: .25;
  background-image: linear-gradient(rgba(255,255,255,.07) 1px, transparent 1px),
                    linear-gradient(90deg, rgba(255,255,255,.07) 1px, transparent 1px);
  background-size: 88px 88px;
  mask-image: radial-gradient(ellipse 90% 70% at 50% 40%, #000 30%, transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse 90% 70% at 50% 40%, #000 30%, transparent 100%);
}
.hero__inner { position: relative; z-index: 2; width: 100%; padding-block: clamp(120px, 16vh, 180px) clamp(52px, 7vh, 84px); }
.hero h1 { color: #fff; max-width: 17ch; }
.hero .lede { color: rgba(255,255,255,.8); max-width: 54ch; margin-top: 24px; }
.hero__actions { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 38px; }
.hero__eyebrow { color: var(--accent-hi); }
.hero__eyebrow::before { background: var(--accent-hi); }

.hero-meta {
  display: flex; flex-wrap: wrap; gap: clamp(28px, 5vw, 64px);
  margin-top: clamp(48px, 7vh, 76px); padding-top: 34px;
  border-top: 1px solid var(--line-dark);
}
.hero-meta div { min-width: 120px; }
.hero-meta b {
  display: block; font-size: clamp(1.9rem, 3.4vw, 2.7rem); font-weight: 800;
  color: #fff; letter-spacing: -0.045em; line-height: 1; font-variant-numeric: tabular-nums;
}
.hero-meta span { display: block; margin-top: 9px; font-size: .78rem; letter-spacing: .12em; text-transform: uppercase; color: rgba(255,255,255,.56); font-weight: 600; }

/* page hero (inner pages) */
.pagehero { position: relative; background: var(--ink); padding-top: calc(var(--nav-h) + clamp(56px, 9vw, 104px)); padding-bottom: clamp(52px, 8vw, 92px); overflow: hidden; }
.pagehero__bg { position: absolute; inset: 0; }
/* 2026-09-30: photograph was at .3 under a flat .92/.78 wash, which left these
   heroes reading as a grey block with a picture somewhere behind it. Same recipe
   as the homepage hero now — the frame at full strength, and a directional scrim
   that is heavy where the copy sits and clears toward the right. */
.pagehero__bg img {
  width: 100%; height: 100%; object-fit: cover;
  opacity: .85; filter: saturate(1.12) contrast(1.06);
}
.pagehero__bg::after {
  content: ''; position: absolute; inset: 0;
  background:
    linear-gradient(100deg, rgba(20,22,24,.92) 0%, rgba(20,22,24,.82) 40%, rgba(20,22,24,.46) 70%, rgba(20,22,24,.2) 100%),
    linear-gradient(180deg, rgba(20,22,24,.5) 0%, rgba(20,22,24,.1) 34%, rgba(20,22,24,.1) 62%, rgba(20,22,24,.55) 100%);
}
@media (max-width: 900px) {
  /* No room to clear to one side on a phone — one vertical wash instead. */
  .pagehero__bg::after {
    background: linear-gradient(180deg, rgba(20,22,24,.72) 0%, rgba(20,22,24,.56) 45%, rgba(20,22,24,.8) 100%);
  }
}
.pagehero .wrap { position: relative; z-index: 2; }
.pagehero h1 { color: #fff; max-width: 18ch; }
.pagehero .lede { color: rgba(255,255,255,.76); margin-top: 20px; max-width: 60ch; }
.crumb { font-size: .8rem; letter-spacing: .1em; text-transform: uppercase; color: rgba(255,255,255,.5); font-weight: 600; margin-bottom: 18px; }
.crumb a:hover { color: var(--accent-hi); }

/* ============ CARDS ============ */
.card {
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden; display: flex; flex-direction: column;
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease);
}
.card:hover { border-color: #CBD5E1; box-shadow: var(--shadow); }

.svc { position: relative; }
.svc__media { position: relative; aspect-ratio: 16/10; overflow: hidden; background: var(--paper-3); }
.svc__media img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--ease), filter .4s var(--ease); filter: saturate(.85); }
.svc:hover .svc__media img { transform: scale(1.05); filter: saturate(1); }
.svc__num {
  position: absolute; top: 14px; left: 14px; z-index: 2;
  background: rgba(7,11,20,.72); backdrop-filter: blur(8px); color: var(--accent-hi);
  font-size: .72rem; font-weight: 700; letter-spacing: .1em; padding: 5px 11px; border-radius: 999px;
  font-variant-numeric: tabular-nums;
}
.svc__body { padding: 24px 24px 26px; display: flex; flex-direction: column; flex: 1; gap: 10px; }
.svc__body h3 { font-size: 1.16rem; }
.svc__body p { font-size: .93rem; line-height: 1.6; color: var(--muted); flex: 1; }
.svc__more { display: inline-flex; align-items: center; gap: 7px; font-size: .86rem; font-weight: 600; color: var(--accent-ink); margin-top: 4px; transition: gap .2s var(--ease); }
.svc:hover .svc__more { gap: 12px; }
.svc__more svg { width: 14px; height: 14px; }
.svc__link { position: absolute; inset: 0; z-index: 3; }

/* pillar */
.pillar { padding: 30px 28px 32px; border-radius: var(--r); background: rgba(255,255,255,.045); border: 1px solid var(--line-dark); transition: background-color .25s var(--ease), border-color .25s var(--ease); }
.pillar:hover { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.22); }
.pillar__icon { width: 42px; height: 42px; border-radius: 10px; background: rgba(255,92,119,.15); color: var(--accent-hi); display: grid; place-items: center; margin-bottom: 20px; }
.pillar__icon svg { width: 21px; height: 21px; }
.pillar h3 { font-size: 1.1rem; margin-bottom: 9px; }
.pillar p { font-size: .93rem; color: rgba(255,255,255,.66); line-height: 1.6; }

/* stat */
.stat { padding: 26px 0; border-top: 2px solid var(--ink); }
.stat b { display: block; font-size: clamp(2.2rem, 4vw, 3.2rem); font-weight: 800; color: var(--ink); letter-spacing: -0.045em; line-height: 1; font-variant-numeric: tabular-nums; }
.stat span { display: block; margin-top: 12px; font-size: .84rem; color: var(--muted); font-weight: 500; line-height: 1.45; }

/* ============ SPLIT ============ */
.split { display: grid; grid-template-columns: 1.05fr 1fr; gap: clamp(32px, 6vw, 84px); align-items: center; }
.split--rev > *:first-child { order: 2; }
@media (max-width: 900px) {
  .split { grid-template-columns: 1fr !important; }
  .split--rev > *:first-child { order: 0; }
  .split > aside { position: static !important; }
}
.split__media { position: relative; border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--shadow-lg); }
.split__media img { width: 100%; aspect-ratio: 4/3; object-fit: cover; }
.split__badge {
  position: absolute; left: 22px; bottom: 22px; z-index: 2;
  background: rgba(255,255,255,.95); backdrop-filter: blur(10px);
  border-radius: var(--r); padding: 16px 22px; box-shadow: var(--shadow);
}
.split__badge b { display: block; font-size: 1.7rem; font-weight: 800; color: var(--ink); letter-spacing: -0.04em; line-height: 1; }
.split__badge span { font-size: .76rem; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); font-weight: 600; }

.checklist { list-style: none; padding: 0; margin: 26px 0 0; display: grid; gap: 13px; }
.checklist li { display: flex; gap: 13px; align-items: flex-start; font-size: .97rem; }
.checklist svg { width: 19px; height: 19px; flex: none; color: var(--accent); margin-top: 3px; }

/* ============ PORTFOLIO ============ */
.filters { display: flex; flex-wrap: wrap; gap: 9px; margin-bottom: 38px; }
.filters button {
  padding: 9px 18px; border-radius: 999px; border: 1.5px solid var(--line);
  background: transparent; color: var(--slate); font-size: .875rem; font-weight: 600;
  cursor: pointer; transition: all .2s var(--ease); min-height: 42px;
}
.filters button:hover { border-color: var(--ink); color: var(--ink); }
.filters button[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: #fff; }
.filters .count { opacity: .55; font-weight: 500; margin-left: 4px; font-variant-numeric: tabular-nums; }

.proj-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(16px, 2vw, 26px); }
@media (max-width: 900px) { .proj-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .proj-grid { grid-template-columns: 1fr; } }

.proj {
  /* 2026-09-29, client request 3.1-3.3: the tile used to be one dark photograph
     with the name printed over it, on a mosaic of mixed footprints. The client
     asked for the photography to stand on its own — no scrim, nothing dimming
     it — with the name and category underneath, on an even three-column grid.
     So the tile is now a picture plus a caption, not a card. */
  position: relative; display: block; cursor: pointer; background: transparent;
}
.proj--static { cursor: default; }
.proj__media {
  position: relative; overflow: hidden;
  aspect-ratio: 4 / 3; border-radius: var(--r); background: var(--paper-3);
}
/* Hover is a straight zoom on the photograph — nothing else moves. The tilt, the
   sheen and (2026-09-29) the shake are all gone; three effects competing on one
   small tile read as noise. */
.proj img {
  width: 100%; height: 100%; object-fit: cover;
  /* opacity was .82 — that was the dark overlay the client asked to remove. */
  opacity: 1;
  transform: scale(1);
  transition: transform .55s var(--ease);
}
.proj:hover img, .proj:focus-visible img { transform: scale(1.04); }
.proj:hover .proj__media, .proj:focus-visible .proj__media { box-shadow: var(--shadow); }

@media (prefers-reduced-motion: reduce) {
  .proj img, .proj:hover img, .proj:focus-visible img { transform: none; }
}

.proj__body { padding: 14px 2px 0; }
.proj__body h3 {
  color: var(--ink); font-size: 1rem; letter-spacing: -0.015em; line-height: 1.3;
}
/* Category sits under the name as a quiet line — it was a red pill printed over
   the photograph. */
.proj__cat {
  display: block; margin: 6px 0 0;
  font-size: .8rem; font-weight: 500; letter-spacing: 0; text-transform: none;
  color: var(--muted); background: transparent; padding: 0; border-radius: 0;
}
.proj__city { display: block; margin-top: 4px; font-size: .78rem; color: var(--brand-grey); }

/* Projects from the client's tracker with no photograph yet. Quiet on the light
   ground, and it must read as "not shot yet", not as a broken card. */
.proj--pending .proj__media {
  background:
    repeating-linear-gradient(135deg, rgba(30,32,34,.028) 0 12px, transparent 12px 24px),
    var(--paper-3);
}
.proj__placeholder {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 11px;
}
.proj__placeholder img { width: 42px; height: 42px; opacity: .3; border-radius: 8px; }
.proj__placeholder em {
  font-style: normal; font-size: .7rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--brand-grey);
}

.proj.is-hidden { display: none; }

/* ============ FORM ============ */
.form { display: grid; gap: 18px; }
.field { display: grid; gap: 7px; }
.field label { font-size: .84rem; font-weight: 600; color: var(--ink); letter-spacing: -0.005em; }
.field label .req { color: #B91C1C; }
.field input, .field select, .field textarea {
  width: 100%; padding: 14px 16px; border: 1.5px solid var(--line); border-radius: var(--r-sm);
  background: var(--paper); font-size: .96rem; color: var(--ink);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
  min-height: 50px;
}
.field textarea { min-height: 132px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 4px rgba(234, 3, 3,.16);
}
.field input::placeholder, .field textarea::placeholder { color: #9AA6B8; }
.field .hint { font-size: .78rem; color: var(--muted); }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 640px) { .form-row { grid-template-columns: 1fr; } }

.form-note { font-size: .82rem; color: var(--muted); }

/* ============ CONTACT BLOCKS ============ */
.cinfo { display: grid; gap: 2px; }
.cinfo a, .cinfo p { display: flex; gap: 16px; align-items: flex-start; padding: 18px 0; border-bottom: 1px solid var(--line-dark); }
.cinfo .ic { width: 40px; height: 40px; border-radius: 10px; background: rgba(255,92,119,.15); color: var(--accent-hi); display: grid; place-items: center; flex: none; }
.cinfo .ic svg { width: 19px; height: 19px; }
.cinfo b { display: block; font-size: .74rem; letter-spacing: .13em; text-transform: uppercase; color: rgba(255,255,255,.5); font-weight: 700; margin-bottom: 3px; }
.cinfo strong { display: block; font-size: 1.04rem; color: #fff; font-weight: 600; letter-spacing: -0.015em; }
.cinfo a:hover strong { color: var(--accent-hi); }
/* 2026-10-15: one award card per project. The first award heads the card; the
   rest are named underneath, because a "+3 more" badge hid real awards behind a
   number the reader could do nothing with. */
.rec__also { list-style: none; margin: 8px 0 0; padding: 8px 0 0; border-top: 1px solid var(--line); display: grid; gap: 5px; }
.rec__also li { position: relative; padding-left: 14px; font-size: .82rem; line-height: 1.4; color: var(--muted); }
.rec__also li::before { content: ""; position: absolute; left: 0; top: .52em; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); opacity: .5; }

.hours { width: 100%; border-collapse: collapse; }
.hours td { padding: 12px 0; border-bottom: 1px solid var(--line); font-size: .95rem; }
.hours td:last-child { text-align: right; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.hours tr.closed td:last-child { color: var(--muted); font-weight: 500; }

/* ============ CTA BAND ============ */
.cta-band { position: relative; background: var(--ink); overflow: hidden; }
.cta-band__bg { position: absolute; inset: 0; }
/* Same treatment. At .22 under a .94 wash the photograph was doing nothing but
   tinting the band — the picture was invisible at the right-hand end where there
   is no copy at all. */
.cta-band__bg img {
  width: 100%; height: 100%; object-fit: cover;
  opacity: .85; filter: saturate(1.12) contrast(1.06);
}
.cta-band__bg::after {
  content: ''; position: absolute; inset: 0;
  background:
    linear-gradient(100deg, rgba(20,22,24,.9) 0%, rgba(20,22,24,.74) 36%, rgba(20,22,24,.32) 68%, rgba(20,22,24,.14) 100%),
    linear-gradient(180deg, rgba(20,22,24,.42) 0%, rgba(20,22,24,.06) 40%, rgba(20,22,24,.5) 100%);
}
@media (max-width: 900px) {
  .cta-band__bg::after {
    background: linear-gradient(180deg, rgba(20,22,24,.74) 0%, rgba(20,22,24,.58) 45%, rgba(20,22,24,.82) 100%);
  }
}
.cta-band .wrap { position: relative; z-index: 2; }

/* ============ FOOTER ============ */
.footer { background: var(--ink); color: rgba(255,255,255,.6); padding-block: clamp(56px, 7vw, 84px) 0; }
.footer h4 { color: #fff; font-size: .78rem; letter-spacing: .15em; text-transform: uppercase; margin-bottom: 20px; font-weight: 700; }
.footer a { transition: color .2s var(--ease); }
.footer a:hover { color: var(--accent-hi); }
.footer__grid { display: grid; grid-template-columns: 1.7fr 1fr 1fr 1.25fr; gap: clamp(28px, 4vw, 56px); }
@media (max-width: 900px) { .footer__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .footer__grid { grid-template-columns: 1fr; } }
.footer ul { list-style: none; padding: 0; display: grid; gap: 11px; font-size: .92rem; }
/* The firm's tagline, used on light and dark grounds alike. */
.tagline {
  font-family: 'Tinos', 'Times New Roman', Georgia, serif;
  font-style: italic; font-size: clamp(1.05rem, 1.7vw, 1.35rem);
  letter-spacing: .01em; color: var(--accent-ink); margin-bottom: 14px;
}
.tagline--onDark { color: var(--accent-hi); }

.footer__brand { display: flex; align-items: center; margin-bottom: 14px; }
.footer__logo { height: 46px; width: auto; }
/* The firm's own tagline — it closes their email signature. Our audit had read it as
   an empty homepage heading; it is in fact the line they sign off with. */
.footer__tagline {
  font-family: 'Tinos', 'Times New Roman', Georgia, serif;
  font-size: 1.02rem; font-style: italic; color: var(--accent-hi);
  letter-spacing: .01em; margin-bottom: 16px;
}
.footer__about { font-size: .93rem; line-height: 1.65; max-width: 38ch; }
.socials { display: flex; gap: 10px; margin-top: 24px; }
.socials a {
  width: 40px; height: 40px; border-radius: 10px; display: grid; place-items: center;
  border: 1px solid var(--line-dark); color: rgba(255,255,255,.72);
  transition: all .2s var(--ease);
}
.socials a:hover { background: var(--accent); border-color: var(--accent); color: var(--ink); }
.socials svg { width: 17px; height: 17px; }
.footer__bar {
  margin-top: clamp(40px, 5vw, 60px); padding-block: 26px;
  border-top: 1px solid var(--line-dark);
  display: flex; flex-wrap: wrap; gap: 14px; justify-content: space-between;
  font-size: .84rem;
}
.badges { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
.badges span {
  font-size: .68rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  border: 1px solid var(--line-dark); color: rgba(255,255,255,.66);
  padding: 7px 12px; border-radius: 999px;
}

/* ============ MISC ============ */
.divider { height: 1px; background: var(--line); border: 0; }
.tag {
  display: inline-block; font-size: .7rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent-ink); background: var(--brand-wash);
  padding: 5px 11px; border-radius: 999px;
}

.accordion { border-top: 1px solid var(--line); }
.accordion__item { border-bottom: 1px solid var(--line); }
.accordion__btn {
  width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 20px;
  padding: 24px 0; background: none; border: 0; cursor: pointer; text-align: left;
  font-size: 1.06rem; font-weight: 600; color: var(--ink); letter-spacing: -0.02em;
}
.accordion__btn svg { width: 20px; height: 20px; flex: none; color: var(--accent); transition: transform .28s var(--ease); }
.accordion__btn[aria-expanded="true"] svg { transform: rotate(45deg); }
.accordion__panel { overflow: hidden; height: 0; transition: height .3s var(--ease); }
.accordion__panel > div { padding-bottom: 26px; font-size: .97rem; max-width: 68ch; }

/* reveal */
/* Scoped to .js: without this, a JS failure or a crawler that does not run
   scripts sees an empty page — every section starts at opacity 0 and only the
   IntersectionObserver ever brings it back. Content first, animation second. */
.js .reveal { opacity: 0; transform: translateY(22px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
.js .reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1 !important; transform: none !important; transition: none; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* 5-up grids collapse */
.grid.cols-5 { grid-template-columns: repeat(5, 1fr); }
@media (max-width: 1000px) { .grid.cols-5 { grid-template-columns: repeat(3, 1fr) !important; } }
@media (max-width: 620px)  { .grid.cols-5 { grid-template-columns: repeat(2, 1fr) !important; } }
a.stat { display: block; transition: border-color .2s var(--ease), color .2s var(--ease); }
a.stat:hover { border-top-color: var(--accent); }
a.stat:hover b { color: var(--accent-ink); }
.pillar.center { text-align: center; }
.sr-only { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; }

/* narrow-phone polish */
@media (max-width: 430px) {
  .brand__logo { height: 30px; }
  .header.is-stuck .brand__logo { height: 28px; }
  .hero__actions .btn { width: 100%; }
  .hero-meta { gap: 24px 32px; }
  .filters button { font-size: .82rem; padding: 8px 14px; }
}

/* ============ PROJECT GALLERY LIGHTBOX ============
   Tiles backed by client photography open a lightbox. Tiles without it stay
   inert, because project detail pages depend on the completed spreadsheet. */
.proj--gallery { border: 0; padding: 0; font: inherit; color: inherit; text-align: left; cursor: pointer; }
.proj--gallery:hover .proj__count { background: var(--accent); color: #fff; }
.proj__count {
  position: absolute; top: 14px; right: 14px; z-index: 3;
  min-width: 30px; height: 24px; padding: 0 9px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  white-space: nowrap; line-height: 1;
  background: rgba(10,12,14,.62); color: #fff;
  font-size: .72rem; font-weight: 700; letter-spacing: .02em;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  transition: background-color .22s var(--ease), color .22s var(--ease);
}
.proj__count::before { content: ''; width: 9px; height: 8px; border: 1.5px solid currentColor; border-radius: 1.5px; opacity: .85; }

.lb {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(10,11,13,.94);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  display: grid; grid-template-rows: 1fr auto;
  padding: clamp(16px, 3vw, 40px);
  animation: lb-in .22s var(--ease);
}
.lb[hidden] { display: none; }
@keyframes lb-in { from { opacity: 0 } to { opacity: 1 } }

.lb__stage {
  /* flex column, not grid: a two-row grid gives the image and the caption a share
     of the free space each, which pushes them apart on tall screens */
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 0; margin: 0; gap: 16px;
}
.lb__img {
  max-width: 100%; max-height: calc(100vh - 260px);
  width: auto; height: auto; object-fit: contain;
  border-radius: var(--r); box-shadow: 0 30px 80px rgba(0,0,0,.55);
}
.lb__cap { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; justify-content: center; text-align: center; }
.lb__title { color: #fff; font-family: 'Tinos','Times New Roman',Georgia,serif; font-size: 1.15rem; }
.lb__city, .lb__counter { color: rgba(255,255,255,.6); font-size: .85rem; }
.lb__counter { font-variant-numeric: tabular-nums; }

.lb__close, .lb__nav {
  position: absolute; z-index: 2;
  width: 48px; height: 48px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.22); background: rgba(255,255,255,.08);
  color: #fff; font-size: 1.5rem; line-height: 1; cursor: pointer;
  display: grid; place-items: center;
  transition: background-color .2s var(--ease), border-color .2s var(--ease);
}
.lb__close:hover, .lb__nav:hover { background: var(--accent); border-color: var(--accent); }
.lb__close { top: clamp(14px,2.5vw,28px); right: clamp(14px,2.5vw,28px); font-size: 1.1rem; }
.lb__nav { top: 50%; transform: translateY(-50%); font-size: 2rem; padding-bottom: 5px; }
.lb__nav--prev { left: clamp(10px,2vw,28px); }
.lb__nav--next { right: clamp(10px,2vw,28px); }
.lb__nav[disabled] { opacity: .28; cursor: default; }
.lb__nav[disabled]:hover { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.22); }

.lb__strip {
  display: flex; gap: 10px; justify-content: center; flex-wrap: nowrap;
  overflow-x: auto; padding: 16px 4px 4px; scrollbar-width: thin;
}
.lb__strip button {
  flex: none; width: 104px; height: 70px; padding: 0; cursor: pointer;
  border: 2px solid transparent; border-radius: 8px; overflow: hidden;
  background: none; opacity: .5;
  transition: opacity .2s var(--ease), border-color .2s var(--ease);
}
.lb__strip button img { width: 100%; height: 100%; object-fit: cover; }
.lb__strip button:hover { opacity: .85; }
.lb__strip button[aria-current="true"] { opacity: 1; border-color: var(--accent); }

body.lb-open { overflow: hidden; }

@media (max-width: 640px) {
  .lb__img { max-height: calc(100vh - 230px); }
  .lb__nav { width: 42px; height: 42px; font-size: 1.6rem; }
  .lb__strip button { width: 76px; height: 54px; }
}
@media (prefers-reduced-motion: reduce) { .lb { animation: none; } }

/* ============ PARTNER LOGOS — 40th anniversary strip ============
   36 marks from 36 practices, at every imaginable size and aspect. */
.partners .marquee { padding-block: 8px; }
.partners .marquee + .marquee { margin-top: 26px; }
/* A touch more room per mark, so the wall reads as a row of logos rather than a
   continuous band of ink. */
.plogo {
  /* flex, not grid: a grid's auto row sizes to the image and lets it overflow
     the box, so max-height resolved against the image instead of this height */
  flex: none; display: flex; align-items: center; justify-content: center;
  height: 84px; padding: 0 10px;
}
.plogo img {
  max-height: 100%; max-width: 230px; width: auto; height: auto;
  object-fit: contain;
  /* Not greyscaled, and no longer held back at 80% either — the client found
     the wall dull, and on a near-white ground that transparency was washing the
     marks out rather than calming them. Full strength, with a little saturation
     and contrast so the coloured marks carry. */
  opacity: 1;
  filter: saturate(1.18) contrast(1.1);
  transition: opacity .3s var(--ease), transform .3s var(--ease);
}
.plogo img:hover { filter: saturate(1.3) contrast(1.14); transform: scale(1.06); }

@media (max-width: 700px) {
  .plogo { height: 64px; padding: 0 6px; }
  .plogo img { max-width: 165px; }
}
@media (prefers-reduced-motion: reduce) {
  .partners .marquee { overflow-x: auto; }
  .plogo img { opacity: 1; }
  .plogo img:hover { transform: none; }
}

/* ============ FORM VALIDATION ============
   Native validation is on (the `novalidate` attribute was removed), so the
   browser blocks submission and messages the field. This makes the offending
   control obvious rather than relying on the tooltip alone. */
.form :is(input, select, textarea):user-invalid {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(234, 3, 3, .12);
}
.form :is(input, select, textarea):user-invalid + .hint { color: var(--accent-ink); }

/* ============ CAREERS — role tabs ============
   Two postings, one form. The form sits in a sticky aside and is never
   re-rendered when the role changes, so a part-filled application survives
   switching between the two. */
.roletabs { display: flex; gap: 10px; flex-wrap: wrap; margin: 4px 0 30px; }
.roletabs button {
  flex: 1 1 220px; text-align: left; cursor: pointer;
  padding: 14px 18px; border-radius: var(--r);
  border: 1.5px solid var(--line); background: var(--paper);
  font-weight: 700; font-size: .95rem; color: var(--muted);
  display: flex; flex-direction: column; gap: 3px;
  transition: border-color .2s var(--ease), color .2s var(--ease), background-color .2s var(--ease);
}
.roletabs button em {
  font-style: normal; font-weight: 500; font-size: .78rem;
  letter-spacing: .02em; color: var(--muted); opacity: .85;
}
.roletabs button:hover { border-color: var(--ink); color: var(--ink); }
.roletabs button[aria-selected="true"] {
  background: var(--ink); border-color: var(--ink); color: #fff;
}
.roletabs button[aria-selected="true"] em { color: rgba(255,255,255,.72); }

.rolepanel h2 { font-size: clamp(1.5rem, 2.6vw, 2.1rem); }
.rolemeta { color: var(--muted); font-weight: 500; margin-top: 8px; }
/* Both panels show without JavaScript; the script hides the inactive one. */
.js .rolepanel[hidden] { display: none; }
.rolepanel + .rolepanel { margin-top: 48px; }
.js .rolepanel + .rolepanel { margin-top: 0; }

@media (max-width: 900px) {
  .roletabs button { flex: 1 1 100%; }
}

/* ============ AWARDS & CERTIFICATIONS ============ */
/* Added 2026-08-28 for awards.html (client request: a dedicated section for
   awards, certifications and recognitions). Built from existing tokens only. */

/* Standing credentials — P.E., LEED AP, ASHRAE. Light ground, brand rule at the
   top so the three read as one set rather than three unrelated cards. */
.cred {
  padding: 30px 28px 32px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-top: 3px solid var(--brand);
  border-radius: var(--r);
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease), transform .25s var(--ease);
}
.cred:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
.cred__label {
  display: inline-block; margin-bottom: 18px;
  font-size: .72rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-ink); background: var(--brand-wash);
  border-radius: 999px; padding: 7px 13px;
}
.cred h3 { font-size: 1.12rem; margin-bottom: 10px; line-height: 1.3; }
.cred p { font-size: .93rem; color: var(--slate); line-height: 1.65; }
/* Organisation cards carry a link out to the body itself. Underline on hover
   only: six cards in a grid with six permanently underlined headings reads as a
   list of links rather than a set of credentials. */
.cred__link { color: inherit; text-decoration: none; }
.cred__link:hover { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; }
.cred__link:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 2px; }

/* Certificate documents — 2026-10-21 rebuild.
   The client asked for the scans big and professional, with no caption under
   each one. So the document IS the tile: a white mat, a hairline frame and a
   soft drop shadow, the way a certificate is framed on a wall. Two to a row on
   a desktop, which puts each scan at roughly 560px — wide enough to read the
   project name and the certification level without opening anything. Three to a
   row would drop that to ~370px and the whole point of the section is lost.
   object-fit:contain throughout, never cover — cropping a certificate cuts the
   seal or the signature off. */
.cert-grid {
  display: grid; gap: clamp(22px, 2.4vw, 34px);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 520px), 1fr));
  align-items: start;             /* documents differ in proportion; don't stretch them */
}
.cert {
  display: block;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: clamp(14px, 1.6vw, 22px);   /* the mat the document sits on */
  box-shadow: var(--shadow-sm);
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease), transform .25s var(--ease);
}
.cert:hover { border-color: var(--brand-grey); box-shadow: var(--shadow-lg); transform: translateY(-4px); }
.cert__doc {
  /* build.py sets the real proportions inline from awards.json; this is only the
     fallback for a document whose dimensions were never recorded. */
  aspect-ratio: 1.27 / 1;
  display: grid; place-items: center;
  background: var(--paper);
}
.cert__doc img {
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  object-fit: contain;
  /* A hairline on the scan itself: most of these certificates are printed on
     near-white stock and would otherwise bleed into the mat. */
  border: 1px solid var(--line);
}
/* A certificate that links to its PDF gets the affordance; one without stays inert. */
a.cert { text-decoration: none; color: inherit; }
a.cert:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
/* The section heading carries a line of context, so it needs the same measure
   the other section ledes get rather than running the full width. */
.certs .section-head .lede { max-width: 62ch; }

@media (max-width: 560px) {
  .cert-grid { grid-template-columns: 1fr; gap: 18px; }
}

/* Portrait media inside a split. The default .split__media crops to 4/3, which
   takes the top of a standing portrait off. Added 2026-09-01 for the principal
   block on the About page. */
.split__media--portrait img { aspect-ratio: 3/4; object-fit: cover; object-position: center 12%; }
@media (min-width: 901px) {
  /* The name and credentials sit under the portrait, so this column has its own
     content rather than trailing off into white space beside a long biography.
     It sticks while the biography scrolls past. */
  .split--portrait { grid-template-columns: 0.82fr 1.18fr; align-items: start; }
  .split--portrait > *:first-child { position: sticky; top: 100px; }
}
.split--portrait .eyebrow { display: flex; }

/* Heading in one column, prose in the other. Full-width prose left the whole
   right half of the section empty, which read as a fault. */
@media (min-width: 901px) {
  .split--copy { grid-template-columns: 0.8fr 1.2fr; align-items: start; gap: clamp(32px, 5vw, 72px); }
  .split--copy > *:first-child { position: sticky; top: 100px; }
}
.split--copy h2 { margin-top: 0; }

/* Badge chips on a light ground; .badges alone is written for the dark footer. */
.badges--light { margin-top: 16px; }
.badges--light span { border-color: var(--line); color: var(--muted); }

/* ============ HERO PAN ============ */
/* Three photographs, each panning horizontally while it holds, cross-fading on
   a 24s loop. Entirely CSS on three <img> tags plus three indicator ticks —
   no slider, no controls, no JavaScript, nothing to initialise.

   The reference the client liked (haritoneng.com) runs Slider Revolution to do
   roughly this. The differences here are deliberate: the pan is a true
   horizontal traverse rather than a drift, the fade and the pan share one
   timing function so the image is never seen changing direction, and the ticks
   report progress without inviting a click. */

.hero__pan { position: absolute; inset: 0; overflow: hidden; }
.hero__pan img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; filter: saturate(1.22) contrast(1.12) brightness(1.04);
  will-change: opacity, transform;
  animation: heroPan 80s linear infinite;
  /* Scale 1.14 leaves 7% of overscan on each side, so a 4% traverse never
     reaches an edge. */
  --from: -4%;
  --to: 4%;
}
/* 2026-10-21: nine frames became ten (one photograph per project, client
   request). Still eight seconds a frame — the cycle went 72s -> 80s and the
   keyframe percentages below were rescaled to match, so the fade and the hold are
   unchanged in seconds. Direction alternates frame to frame so consecutive passes
   never read as the same gesture repeated, and the traverse shortens slightly
   down the list to keep the wider elevations from swimming. */
.hero__pan img:nth-child(1) { animation-delay:  0s; --from:   -4%; --to:    4%; }
.hero__pan img:nth-child(2) { animation-delay:  8s; --from:  3.6%; --to: -3.6%; }
.hero__pan img:nth-child(3) { animation-delay: 16s; --from: -3.4%; --to:  3.4%; }
.hero__pan img:nth-child(4) { animation-delay: 24s; --from:  3.8%; --to: -3.8%; }
.hero__pan img:nth-child(5) { animation-delay: 32s; --from: -3.2%; --to:  3.2%; }
.hero__pan img:nth-child(6) { animation-delay: 40s; --from:  3.4%; --to: -3.4%; }
.hero__pan img:nth-child(7) { animation-delay: 48s; --from: -3.6%; --to:  3.6%; }
.hero__pan img:nth-child(8) { animation-delay: 56s; --from:  3.2%; --to: -3.2%; }
.hero__pan img:nth-child(9) { animation-delay: 64s; --from: -3.8%; --to:  3.8%; }
.hero__pan img:nth-child(10) { animation-delay: 72s; --from:  3.4%; --to: -3.4%; }

@keyframes heroPan {
  /* Percentages are seconds over the 80s cycle, because the cycle went 72s -> 80s
     when the hero went to ten frames. In seconds nothing moved: 1.2s fade in,
     ~7s hold, out by 8.16s. */
  0%      { opacity: 0;   transform: scale(1.14) translate3d(var(--from), 0, 0); }
  1.5%    { opacity: .88; }
  8.7%    { opacity: .88; }
  /* The traverse finishes exactly as the frame leaves, so it is never seen
     snapping back to its start. */
  10.2%   { opacity: 0;   transform: scale(1.14) translate3d(var(--to), 0, 0); }
  100%    { opacity: 0; }
}

/* Two scrims, not one flat wash: the wash was tuned for a single dark
   photograph and turned the daylight frames grey. */
/* Scrim back, lighter again: roughly two thirds of what it was before today.
   The photograph reads across most of the frame while the copy still has ground
   under it — the text-shadow below covers the rest, which matters because the
   frames change every eight seconds and what sits behind a line is not fixed. */
.hero__pan::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(100deg, rgba(20,22,24,.62) 0%, rgba(20,22,24,.46) 34%, rgba(20,22,24,.12) 64%, rgba(20,22,24,0) 100%),
    linear-gradient(180deg, rgba(20,22,24,.4) 0%, rgba(20,22,24,0) 26%, rgba(20,22,24,0) 62%, rgba(20,22,24,.6) 100%);
}

/* ---- progress ticks ---- */
/* Three marks, the live one filling in brand red across its eight seconds.
   Non-interactive by design: the client did not want arrows, and a control that
   cannot be clicked should not look like one. */
.hero__ticks {
  position: absolute; bottom: 30px; z-index: 6;
  left: 50%; transform: translateX(-50%);
  width: min(100% - (var(--gutter) * 2), var(--wrap) - (var(--gutter) * 2));
  display: flex; justify-content: flex-end; gap: 9px;
  pointer-events: none;
}
.hero__ticks i {
  display: block; width: 48px; height: 2px;
  background: rgba(255,255,255,.2); position: relative; overflow: hidden;
}
.hero__ticks i::after {
  content: ''; position: absolute; inset: 0;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  animation: heroTick 80s linear infinite;
}
.hero__ticks i:nth-child(1)::after { animation-delay:  0s; }
.hero__ticks i:nth-child(2)::after { animation-delay:  8s; }
.hero__ticks i:nth-child(3)::after { animation-delay: 16s; }
.hero__ticks i:nth-child(4)::after { animation-delay: 24s; }
.hero__ticks i:nth-child(5)::after { animation-delay: 32s; }
.hero__ticks i:nth-child(6)::after { animation-delay: 40s; }
.hero__ticks i:nth-child(7)::after { animation-delay: 48s; }
.hero__ticks i:nth-child(8)::after { animation-delay: 56s; }
.hero__ticks i:nth-child(9)::after { animation-delay: 64s; }
.hero__ticks i:nth-child(10)::after { animation-delay: 72s; }
@keyframes heroTick {
  /* 8s of the 80s cycle — one tick fills over its frame's eight seconds,
     exactly as before. */
  0%       { transform: scaleX(0); }
  10%      { transform: scaleX(1); }
  10.02%   { transform: scaleX(0); }
  100%     { transform: scaleX(0); }
}

@media (max-width: 900px) {
  .hero__pan::after {
    background: linear-gradient(180deg, rgba(20,22,24,.58) 0%, rgba(20,22,24,.36) 40%, rgba(20,22,24,.7) 100%);
  }
  .hero__ticks { bottom: 20px; }
  .hero__ticks i { width: 34px; }
}

@media (prefers-reduced-motion: reduce) {
  .hero__pan img { animation: none; opacity: 0; transform: scale(1.02); }
  .hero__pan img:nth-child(1) { opacity: 1; }
  .hero__ticks { display: none; }
}

/* ============ PORTFOLIO MOSAIC ============ */
/* The collage — four tile footprints, fixed 190px rows, a shake on pointer
   entry — was added 2026-09-03 and retired 2026-09-29 at the client's request:
   "explore a consistent 3-column grid instead of the current mixed-size layout
   ... more like a curated architecture/engineering portfolio and less like a
   photo gallery." The hover card that raises the full photograph stays.

   Rows are auto-height now, because each tile is a 4/3 picture with a caption
   under it and a fixed row height clipped the caption behind the next row. */
.proj-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: auto;
  grid-auto-flow: row;
  gap: clamp(18px, 2.2vw, 30px);
}
@media (max-width: 900px) { .proj-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .proj-grid { grid-template-columns: 1fr; } }

.proj {
  grid-column: auto; grid-row: auto;
  height: auto;
  cursor: default;                 /* nothing here is clickable any more */
}
/* The lift belongs to the picture, not to the whole tile — a shadow under the
   caption text reads as a floating label. */
.proj__media { transition: box-shadow .3s var(--ease); }
.proj-grid .proj { isolation: isolate; }

/* The hover card's rules went with the card itself, 2026-09-30. */
}

/* ---- Awards & Recognition ---- */
/* Added 2026-09-03 with the rename from "Certifications". Recognition is listed
   separately from the awards we physically hold, because we hold a certificate
   for one of them and only the client's word for the rest. */
.recog-grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}
.recog {
  padding: 22px 22px 24px; background: var(--paper);
  border: 1px solid var(--line); border-left: 3px solid var(--brand);
  border-radius: var(--r);
  transition: box-shadow .25s var(--ease), transform .25s var(--ease);
}
.recog:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
.recog b { display: block; font-size: 1rem; line-height: 1.3; margin-bottom: 7px; }
.recog span { font-size: .84rem; color: var(--muted); line-height: 1.5; }

.feat {
  padding: 26px 26px 28px; background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--r-lg);
}
.feat h3 { font-size: 1.12rem; margin-bottom: 8px; line-height: 1.3; }
.feat__line {
  margin: 0 0 14px; font-size: .78rem; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--accent-ink);
}
.feat__detail { margin: 0; font-size: .92rem; color: var(--slate); line-height: 1.65; }

/* Honeypot field: off-screen, never focusable, never seen. */
.hp { position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden; }

/* Year badge, restyled 2026-09-03. The white card sat on the photograph like a
   sticker; this sits in it — a dark glass panel with the brand rule, so the
   number reads as part of the image rather than pasted over it. */
.split__badge--glass {
  left: 0; bottom: 0; right: 0;
  background: linear-gradient(to top, rgba(18,20,22,.92) 0%, rgba(18,20,22,.72) 55%, rgba(18,20,22,0) 100%);
  backdrop-filter: blur(2px);
  border-radius: 0; box-shadow: none;
  padding: 46px 26px 22px;
  display: flex; align-items: baseline; gap: 14px;
}
.split__badge--glass b {
  position: relative; color: #fff;
  font-size: clamp(2.1rem, 4vw, 3rem); font-weight: 800;
  letter-spacing: -0.045em; line-height: 1;
  font-variant-numeric: tabular-nums;
  padding-left: 16px;
}
.split__badge--glass b::before {
  content: ''; position: absolute; left: 0; top: 6%; bottom: 6%;
  width: 3px; background: var(--accent); border-radius: 2px;
}
.split__badge--glass span {
  color: rgba(255,255,255,.7); font-size: .72rem; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase;
}
@media (max-width: 560px) {
  .split__badge--glass { padding: 36px 18px 16px; }
}

/* Source links under a featured project. */
.feat__srcs { margin-top: 18px; display: flex; flex-direction: column; gap: 9px; align-items: flex-start; }
.feat__src {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: .85rem; font-weight: 600; color: var(--accent-ink);
  border-bottom: 1.5px solid transparent; padding-bottom: 2px;
  transition: gap .22s var(--ease), border-color .22s var(--ease);
}
.feat__src svg { width: 15px; height: 15px; flex: none; }
.feat__src:hover { gap: 12px; border-bottom-color: var(--accent); }
.feat__src:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* 2026-10-21: the .cert-grid--single layout that used to live here (one
   certificate shown beside its citation) went with the captions. With eleven
   documents on file the grid is the layout. */

/* Cards in a row should end level. Without this the shortest card leaves a
   ragged gap under it and the row reads as unfinished. */
.grid.cols-3 > .feat, .recog-grid > .recog { height: 100%; }
.feat { display: flex; flex-direction: column; }
.feat__detail { flex: 1; }            /* pushes the source links to the foot */
/* Top-aligned, not centred: centring made shorter cards float their text and
   the row read as misaligned even though the cards were level. */
.recog { display: flex; flex-direction: column; justify-content: flex-start; }

/* A recognition card that links to the awarding body. The arrow marks it as
   leaving the site; the card itself is the target, so there is no small link to
   aim at. */
.recog--link { position: relative; text-decoration: none; color: inherit; display: block; }
.recog--link:hover { border-color: #CBD5E1; }
.recog--link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.recog__ext {
  position: absolute; top: 14px; right: 14px;
  width: 15px; height: 15px; color: var(--muted);
  opacity: 0; transform: translate3d(-3px,3px,0);
  transition: opacity .22s var(--ease), transform .22s var(--ease), color .22s var(--ease);
}
.recog__ext svg { width: 100%; height: 100%; display: block; }
.recog--link:hover .recog__ext,
.recog--link:focus-visible .recog__ext { opacity: 1; transform: none; color: var(--accent); }
.recog--link b { padding-right: 20px; }   /* clear of the arrow */

/* The intro lost its media column on 2026-09-03, so there is nothing to split
   against. One column, held to a readable measure rather than run edge to edge. */
.split--single { grid-template-columns: 1fr; }
.split--single > * { max-width: 68ch; }

/* ---- emphasis, added 2026-09-08 at the client's request ---- */
/* The firm's name and the "40+ years" figure are picked out wherever they
   appear in body copy. Applied by build.py over text nodes only, and only
   inside <main> — the nav, footer and credential marquee repeat both and
   highlighting them there would be noise rather than emphasis. */
.hl-yrs {
  font-weight: 700; color: var(--accent-ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.hl-firm { font-weight: 600; color: var(--ink); }
/* On a dark ground the ink colour would vanish and the red goes muddy.
   .pagehero was missing from this list, so the firm's name sat ink-on-ink under
   the hero's dark overlay on About and the "40+ years" figure went muddy on
   Awards — invisible in both cases (reported 2026-09-08). .on-dark is the hook
   for the dark panels that carry their ground inline rather than by section
   class, e.g. the sticky contact card. */
.section--dark .hl-firm, .hero .hl-firm, .cta-band .hl-firm,
.pagehero .hl-firm, .on-dark .hl-firm { color: #fff; }
.section--dark .hl-yrs, .hero .hl-yrs, .cta-band .hl-yrs,
.pagehero .hl-yrs, .on-dark .hl-yrs { color: var(--accent-hi); }

/* With no scrim the copy sits straight on the photograph, and the frames change
   every eight seconds — so what is behind any given line is not fixed. A shadow
   travels with the type and holds it legible over whichever frame is up. */
.hero__inner .eyebrow,
.hero__inner .tagline,
.hero h1,
.hero .lede,
.hero-meta b,
.hero-meta span,
/* 2026-09-30: the page heroes and the closing bands now show their photograph at
   the same strength as the homepage hero, so they need the same insurance. The
   Awards hero is the case that proved it — feature-1.jpg is a bright bathroom and
   the lede sat on the light half of it. */
.pagehero .crumb,
.pagehero h1,
.pagehero .lede,
.cta-band h2,
.cta-band .lede { text-shadow: 0 1px 2px rgba(0,0,0,.55), 0 3px 18px rgba(0,0,0,.45); }
.hero-meta { border-top-color: rgba(255,255,255,.28); }

/* ============ CLIENT FEEDBACK ============ */
/* Replaced the statement block on 2026-09-08. Five cards, the first spanning two
   columns so the row does not read as a uniform tile grid. */
.tq-grid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.tq {
  margin: 0; padding: 30px 30px 28px;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r-lg);
  display: flex; flex-direction: column;
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease), transform .25s var(--ease);
}
.tq:hover { border-color: #C3C9CE; box-shadow: var(--shadow); transform: translateY(-2px); }
.tq:first-child { grid-column: span 2; }
@media (max-width: 860px) { .tq:first-child { grid-column: span 1; } }

.tq__mark { width: 30px; height: 30px; color: var(--accent); opacity: .9; margin-bottom: 16px; }
.tq blockquote {
  margin: 0 0 20px; flex: 1;
  font-size: 1.02rem; line-height: 1.65; color: var(--ink);
}
.tq:first-child blockquote { font-size: 1.18rem; }
.tq figcaption { border-top: 1px solid var(--line); padding-top: 15px; }
.tq figcaption b { display: block; font-size: .92rem; color: var(--ink); }
.tq figcaption span { display: block; margin-top: 4px; font-size: .84rem; color: var(--muted); }

/* Visible while testimonials.json has placeholder:true, so nobody reviewing the
   staging site mistakes written-to-fit copy for real client feedback. */
.tq-placeholder {
  display: inline-flex; align-items: center; gap: 8px;
  margin: 14px 0 0; padding: 7px 14px; border-radius: 999px;
  background: #FFF4E5; border: 1px solid #F0C98A;
  color: #8A5A00; font-size: .78rem; font-weight: 600; letter-spacing: .02em;
}
.tq-placeholder::before { content: '⚠'; font-size: .9rem; }

/* ============ WHAT WE DO — EDITORIAL INDEX (2026-09-09) ============
   Replaces the ten-tile services grid on the homepage. Volume comes from
   scale and the brand red, not from new colours: every value below is an
   existing token. The row wipe is --ink, the index and caption rule are
   --accent / --accent-hi, both already cleared for white text.            */
.svcx__head {
  display: grid;
  grid-template-columns: minmax(0, 1.06fr) minmax(0, .84fr);
  gap: clamp(24px, 4vw, 64px);
  align-items: end;
  margin-bottom: clamp(34px, 4.2vw, 54px);
}
.svcx__head h2 { font-size: clamp(2.05rem, 4.4vw, 3.3rem); }
.svcx__intro { padding-bottom: 4px; }
@media (max-width: 900px) {
  .svcx__head { grid-template-columns: 1fr; align-items: start; }
  .svcx__br { display: none; }
}

.svcx__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
}

.svcx__list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.svcx__row { position: relative; border-bottom: 1px solid var(--line); }
/* The wipe sits behind the link and bleeds past the text so the active row
   reads as a bar, not as a box drawn around the words. */
.svcx__row::before {
  content: '';
  position: absolute; inset: 0;
  background: var(--ink);
  border-radius: var(--r-sm);
  transform: scaleX(0); transform-origin: left;
  transition: transform .42s var(--ease);
}
.svcx__hit {
  position: relative; z-index: 2;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start; gap: clamp(14px, 2vw, 26px);
  padding: clamp(15px, 1.7vw, 21px) clamp(14px, 1.6vw, 22px);
  cursor: pointer;
}
.svcx__num {
  font-size: .78rem; font-weight: 700; letter-spacing: .16em;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  padding-top: .68em;          /* sits on the first line of the title, not the row */
  transition: color .3s var(--ease);
}
/* An inline box would let the heading inside set the column's min-content
   width and push the row past the viewport — the min-width:auto trap. */
.svcx__text { display: block; min-width: 0; }
.svcx__name {
  font-size: clamp(1.22rem, 2.1vw, 1.92rem);
  line-height: 1.16;
  transition: color .3s var(--ease);
}
.svcx__desc {
  display: grid; grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows .42s var(--ease), opacity .3s var(--ease);
}
.svcx__desc > span {
  display: block; overflow: hidden;
  font-size: .93rem; line-height: 1.6; max-width: 54ch;
  color: rgba(255,255,255,.74);
}
.svcx__arrow {
  width: 44px; height: 44px; border-radius: 999px;
  align-self: center;
  display: grid; place-items: center;
  border: 1.5px solid var(--line); color: var(--ink);
  transition: background-color .3s var(--ease), border-color .3s var(--ease), color .3s var(--ease);
}
.svcx__arrow svg { width: 17px; height: 17px; }
.svcx__thumb { display: none; }

.svcx__row.is-on::before { transform: none; }
.svcx__row.is-on .svcx__num { color: var(--accent-hi); }
.svcx__row.is-on .svcx__name { color: #fff; }
.svcx__row.is-on .svcx__desc { grid-template-rows: 1fr; opacity: 1; }
.svcx__row.is-on .svcx__desc > span { padding-top: 9px; }
.svcx__row.is-on .svcx__arrow { background: var(--accent); border-color: var(--accent); color: #fff; }

/* Without JS nothing ever becomes active, so the descriptions must not be
   the only place the copy lives. */
html:not(.js) .svcx__desc { grid-template-rows: 1fr; opacity: 1; }
html:not(.js) .svcx__desc > span { color: var(--muted); padding-top: 9px; }
html:not(.js) .svcx__row.is-on .svcx__desc > span { color: rgba(255,255,255,.74); }

.svcx__stage {
  position: sticky; top: calc(var(--nav-h) + 26px);
  aspect-ratio: 3 / 4;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--ink);
  box-shadow: var(--shadow);
}
.svcx__plate {
  position: absolute; inset: 0; margin: 0;
  opacity: 0; visibility: hidden;
  transform: scale(1.045);
  transition: opacity .5s var(--ease), transform .9s var(--ease), visibility .5s;
}
.svcx__plate.is-on { opacity: 1; visibility: visible; transform: none; }
.svcx__plate img { width: 100%; height: 100%; object-fit: cover; }
.svcx__tab {
  position: absolute; top: 0; left: 0;
  background: var(--accent); color: #fff;
  font-family: 'Tinos', 'Times New Roman', Georgia, serif;
  font-size: 1.42rem; line-height: 1;
  padding: 13px 19px 14px;
  border-bottom-right-radius: var(--r);
}
.svcx__plate figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  padding: 46px 22px 20px;
  background: linear-gradient(to top, rgba(20,21,23,.88), rgba(20,21,23,0));
}
.svcx__plate figcaption b {
  font-family: 'Tinos', 'Times New Roman', Georgia, serif;
  font-size: 1.18rem; font-weight: 600; color: #fff; letter-spacing: -.01em;
}
.svcx__plate figcaption span {
  font-size: .7rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent-hi); white-space: nowrap;
}

.svcx__band {
  position: relative; overflow: hidden;
  margin-top: clamp(28px, 3.4vw, 46px);
  padding: clamp(26px, 3.2vw, 38px) clamp(24px, 3.4vw, 44px);
  background: var(--ink); border-radius: var(--r-lg);
  color: rgba(255,255,255,.7);
  display: flex; align-items: center; justify-content: space-between;
  gap: clamp(18px, 3vw, 40px); flex-wrap: wrap;
}
.svcx__band::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 5px;
  background: var(--accent);
}
.svcx__band h3 { color: #fff; font-size: clamp(1.25rem, 2vw, 1.6rem); }
.svcx__band p { margin-top: 8px; font-size: .95rem; max-width: 56ch; }
.svcx__band .btn { flex: none; }

@media (max-width: 900px) {
  /* No hover on a phone, so the plate is dropped and every row keeps its own
     photograph and its own paragraph. */
  .svcx__grid { grid-template-columns: 1fr; gap: 0; }
  .svcx__stage { display: none; }
  .svcx__list { border-top: 0; display: grid; gap: 16px; }
  .svcx__row {
    border: 1px solid var(--line); border-radius: var(--r);
    overflow: hidden; background: var(--paper);
  }
  .svcx__row::before { display: none; }
  .svcx__thumb { display: block; width: 100%; aspect-ratio: 16 / 10; object-fit: cover; }
  .svcx__hit { align-items: start; padding: 18px 18px 20px; }
  .svcx__num, .svcx__row.is-on .svcx__num { color: var(--accent-ink); padding-top: 6px; }
  .svcx__name, .svcx__row.is-on .svcx__name { color: var(--ink); font-size: 1.3rem; }
  .svcx__desc, .svcx__row.is-on .svcx__desc { grid-template-rows: 1fr; opacity: 1; }
  .svcx__desc > span, .svcx__row.is-on .svcx__desc > span { color: var(--muted); padding-top: 8px; }
  .svcx__arrow, .svcx__row.is-on .svcx__arrow {
    width: 38px; height: 38px; background: transparent;
    border-color: var(--line); color: var(--accent-ink);
  }
  .svcx__band { flex-direction: column; align-items: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  .svcx__row::before, .svcx__desc, .svcx__plate, .svcx__arrow, .svcx__name, .svcx__num {
    transition: none;
  }
}

/* ============ SELECTED WORK — MOSAIC (2026-09-09) ============
   Homepage only. .proj / .proj-grid stay exactly as they are: the portfolio
   page and its category filter are built on them.                         */
.wk__head {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: clamp(24px, 4vw, 40px); flex-wrap: wrap;
  margin-bottom: clamp(32px, 4vw, 48px);
}
.wk__head > div { max-width: 680px; }
.wk__head h2 { font-size: clamp(2rem, 4.2vw, 3.1rem); }

.wk__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: clamp(170px, 16.5vw, 224px);
  gap: clamp(12px, 1.4vw, 18px);
}
/* The lead runs two columns and two rows; the wides run two columns. Every
   tile keeps its own photograph at a size worth printing. */
.wk__tile--lead { grid-column: span 2; grid-row: span 2; }
.wk__tile--wide { grid-column: span 2; }

.wk__tile {
  position: relative; overflow: hidden;
  border-radius: var(--r);
  background: var(--ink);
  isolation: isolate;
}
.wk__tile img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .7s var(--ease);
}
.wk__tile:hover img, .wk__tile:focus-visible img { transform: scale(1.06); }
.wk__veil {
  position: absolute; inset: 0;
  /* Deep enough that the red sector label stays legible over a bright sky —
     the short tiles put that label high, where a 34% start was still daylight. */
  background: linear-gradient(180deg, rgba(12,14,16,0) 22%, rgba(12,14,16,.58) 58%, rgba(12,14,16,.95) 100%);
  transition: opacity .4s var(--ease);
}
.wk__body { position: absolute; inset: auto 0 0 0; z-index: 2; padding: 20px 22px 22px; }
.wk__cat {
  display: block; margin-bottom: 9px;
  font-size: .7rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent-hi);
  text-shadow: 0 1px 10px rgba(12,14,16,.6);
}
.wk__name {
  color: #fff; font-size: 1.06rem; line-height: 1.24;
  /* The rule is drawn under the name and only as wide as the name. */
  padding-bottom: 10px; position: relative;
}
.wk__name::after {
  content: ''; position: absolute; left: 0; bottom: 0; height: 2px; width: 100%;
  background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform .45s var(--ease);
}
.wk__tile:hover .wk__name::after, .wk__tile:focus-visible .wk__name::after { transform: none; }
.wk__city { display: block; margin-top: 9px; font-size: .82rem; color: rgba(255,255,255,.72); }
.wk__go {
  position: absolute; top: 16px; right: 16px; z-index: 2;
  width: 40px; height: 40px; border-radius: 999px;
  display: grid; place-items: center;
  background: var(--accent); color: #fff;
  opacity: 0; transform: translateY(-6px);
  transition: opacity .3s var(--ease), transform .3s var(--ease);
}
.wk__go svg { width: 16px; height: 16px; }
.wk__tile:hover .wk__go, .wk__tile:focus-visible .wk__go { opacity: 1; transform: none; }

.wk__tile--lead .wk__body { padding: 26px 28px 28px; }
.wk__tile--lead .wk__name { font-size: clamp(1.4rem, 2.3vw, 2rem); }
.wk__tile--lead .wk__cat { font-size: .72rem; }

@media (max-width: 1000px) {
  .wk__grid { grid-template-columns: repeat(2, 1fr); grid-auto-rows: clamp(150px, 24vw, 210px); }
  .wk__tile--wide { grid-column: span 2; }
}
@media (max-width: 620px) {
  .wk__grid { grid-template-columns: 1fr; grid-auto-rows: auto; }
  .wk__tile, .wk__tile--lead, .wk__tile--wide { grid-column: auto; grid-row: auto; aspect-ratio: 4 / 3; }
  .wk__go { opacity: 1; transform: none; }   /* nothing hovers on a phone */
  .wk__name::after { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .wk__tile img, .wk__name::after, .wk__go { transition: none; }
  .wk__tile:hover img { transform: none; }
}

/* ============ SECTORS — MARKET CARDS (2026-09-09) ============ */
/* 2026-10-16: nine sectors, not eight — three up fills 3x3 exactly, where four up
   left a single card alone on the last row. */
.mk__grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(14px, 1.8vw, 24px);
}
.mk__card {
  position: relative;
  padding: 26px 24px 28px;
  border-radius: var(--r);
  background: var(--paper);
  border: 1px solid var(--line);
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease), transform .25s var(--ease);
}
.mk__card:hover { border-color: #CBD5E1; box-shadow: var(--shadow); transform: translateY(-3px); }
.mk__rule {
  position: absolute; top: -1px; left: 24px; right: 24px; height: 3px;
  background: var(--ink);
  transition: background-color .25s var(--ease);
}
.mk__card:hover .mk__rule { background: var(--accent); }
.mk__idx {
  display: block; margin-bottom: 12px;
  font-size: .72rem; font-weight: 700; letter-spacing: .16em;
  font-variant-numeric: tabular-nums; color: var(--muted);
}
.mk__name { font-size: clamp(1.35rem, 2.1vw, 1.75rem); line-height: 1.14; }
.mk__desc { margin-top: 12px; font-size: .88rem; line-height: 1.55; color: var(--muted); }

/* Housing is inverted because its own sentence calls it the largest body of
   work — the emphasis is the copy's, not a decoration. */
.mk__card--lead { background: var(--ink); border-color: var(--ink); }
.mk__card--lead .mk__rule { background: var(--accent); }
.mk__card--lead .mk__idx { color: var(--accent-hi); }
.mk__card--lead .mk__name { color: #fff; }
.mk__card--lead .mk__desc { color: rgba(255,255,255,.76); }
.mk__card--lead:hover { border-color: var(--ink); }

@media (max-width: 1000px) { .mk__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px)  { .mk__grid { grid-template-columns: 1fr; } }
@media (prefers-reduced-motion: reduce) {
  .mk__card, .mk__rule { transition: none; }
  .mk__card:hover { transform: none; }
}

/* ============ MOTION COST FIXES (2026-09-29) ============
   The hero animation is the most expensive thing on the page: three
   full-bleed images, each with will-change and a filter, on a 24s infinite
   loop. It used to keep running the whole time the page was open, whether or
   not the hero was on screen. .is-paused is set by effects.js once the hero
   scrolls away, and it also releases the three compositor layers — a
   will-change left on a large idle element holds GPU memory for nothing. */
.hero__pan.is-paused img {
  animation-play-state: paused;
  will-change: auto;
}
.hero__ticks.is-paused i::after { animation-play-state: paused; }

/* Scaling an image inside a rounded, overflow:hidden tile makes the browser
   repaint the clip every frame unless the image is on its own layer. Promoted
   only while the tile is hovered, so at most one tile holds a layer. */
.wk__tile:hover img, .wk__tile:focus-within img { will-change: transform; }

/* Same reason on the services plate, and only for the one that is showing. */
.svcx__plate.is-on { will-change: opacity, transform; }

/* ============ CLIENT FEEDBACK — COMPACT RAIL (2026-09-29) ============
   Replaces the two-row .tq-grid. The old block ran about a screen and a half;
   this is one row beside the heading. .tq / .tq-grid stay in the sheet — they
   are what the section falls back to if this is ever reverted. */
.fb__grid {
  display: grid;
  grid-template-columns: minmax(0, 290px) minmax(0, 1fr);
  gap: clamp(26px, 4vw, 56px);
  align-items: start;
}
.fb__intro h2 { font-size: clamp(1.7rem, 3vw, 2.4rem); }
.fb__lede { margin-top: 14px; font-size: .95rem; color: var(--muted); line-height: 1.6; }
.fb__intro .tq-placeholder { margin-top: 16px; }

.fb__nav { display: flex; gap: 10px; margin-top: 24px; }
.fb__btn {
  width: 42px; height: 42px; border-radius: 999px;
  border: 1.5px solid var(--line); background: transparent; color: var(--ink);
  display: grid; place-items: center; cursor: pointer;
  transition: background-color .22s var(--ease), border-color .22s var(--ease), color .22s var(--ease);
}
.fb__btn svg { width: 17px; height: 17px; }
.fb__btn:hover { background: var(--ink); border-color: var(--ink); color: #fff; }
.fb__btn[disabled] { opacity: .35; cursor: default; }
.fb__btn[disabled]:hover { background: transparent; border-color: var(--line); color: var(--ink); }

.fb__rail {
  /* Flex, not grid: a percentage in grid-auto-columns resolves against an
     indefinite width inside a horizontal scroller and the cards collapse to
     content width — five narrow columns instead of three readable ones. A
     flex-basis percentage resolves against the container and behaves. */
  display: flex;
  gap: 18px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  padding-bottom: 4px;
}
.fb__rail::-webkit-scrollbar { display: none; }
.fb__card {
  flex: 0 0 calc((100% - 36px) / 3);
  min-width: 250px;
  scroll-snap-align: start;
  display: flex; flex-direction: column; gap: 12px;
  margin: 0; padding: 22px 20px 20px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--paper);
}
.fb__mark { width: 22px; height: 22px; color: var(--accent); }
.fb__card blockquote { margin: 0; font-size: .89rem; line-height: 1.62; color: var(--slate); flex: 1; }
.fb__card figcaption { border-top: 1px solid var(--line); padding-top: 14px; }
.fb__card figcaption b { display: block; font-size: .88rem; color: var(--ink); }
.fb__card figcaption span { display: block; margin-top: 2px; font-size: .8rem; color: var(--muted); }
/* Sample pairings while the copy is placeholder — greyed so they read as marks,
   not as an endorsement from that practice. */
.fb__logo {
  height: 20px; width: auto; max-width: 130px; object-fit: contain;
  margin-bottom: 10px; filter: grayscale(1); opacity: .62;
}
@media (max-width: 1000px) { .fb__card { flex-basis: calc((100% - 18px) / 2); } }
@media (max-width: 860px) {
  .fb__grid { grid-template-columns: 1fr; }
  .fb__nav { margin-top: 18px; }
}
@media (max-width: 560px) { .fb__card { flex-basis: 84%; min-width: 0; } }

/* ============ SELECTED AWARDS & RECOGNITION (2026-09-29) ============ */
.aw__head {
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .9fr);
  gap: clamp(24px, 4vw, 56px); align-items: end;
  margin-bottom: clamp(28px, 3.6vw, 46px);
}
.aw__head h2 { font-size: clamp(1.9rem, 3.8vw, 2.9rem); }
.aw__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(14px, 1.6vw, 22px); }
.aw__card {
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--paper); overflow: hidden;
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease), transform .25s var(--ease);
}
.aw__card:hover { border-color: #CBD5E1; box-shadow: var(--shadow); transform: translateY(-3px); }
.aw__media { aspect-ratio: 4 / 3; overflow: hidden; background: var(--paper-3); }
.aw__media img { width: 100%; height: 100%; object-fit: cover; }
/* Three of the four projects have no photograph yet. Saying so on the tile is
   better than cropping in something that is not the building. */
.aw__pending {
  display: grid; place-items: center; align-content: center; gap: 10px;
  height: 100%; background: var(--paper-3);
}
.aw__pending img { width: 40px; height: auto; opacity: .3; }
.aw__pending em {
  font-style: normal; font-size: .72rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--brand-grey);
}
.aw__body { display: flex; flex-direction: column; gap: 8px; padding: 18px 18px 20px; flex: 1; }
.aw__award {
  font-size: .68rem; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
  color: var(--accent-ink); line-height: 1.35;
}
.aw__title { font-size: 1.1rem; line-height: 1.2; }
.aw__meta { font-size: .84rem; line-height: 1.45; color: var(--muted); }
.aw__detail { font-size: .86rem; line-height: 1.55; color: var(--slate); flex: 1; }
.aw__links { display: flex; flex-direction: column; align-items: flex-start; gap: 9px; margin-top: 6px; }
.aw__links .link-arrow { font-size: .84rem; }
/* 2026-10-15: the homepage grid went from four projects to eleven. Eleven leaves
   a hole in the last row of a four-up grid, so a twelfth cell links to the awards
   page — 12 fills 4x3, 2x6 and 1x12 exactly, so no width orphans a row. */
.aw__also { list-style: none; margin: 0; padding: 8px 0 0; border-top: 1px solid var(--line); display: grid; gap: 5px; }
.aw__also li { position: relative; padding-left: 13px; font-size: .79rem; line-height: 1.4; color: var(--muted); }
.aw__also li::before { content: ""; position: absolute; left: 0; top: .5em; width: 4px; height: 4px; border-radius: 50%; background: var(--accent); opacity: .5; }
.aw__card--cta {
  display: flex; flex-direction: column; justify-content: center; gap: 10px;
  padding: 26px 22px; text-align: left; background: var(--ink); border-color: var(--ink);
  text-decoration: none; min-height: 240px;
}
.aw__card--cta:hover { border-color: var(--ink); }
.aw__cta-eyebrow {
  font-size: .72rem; letter-spacing: .15em; text-transform: uppercase;
  font-weight: 700; color: var(--accent-hi);
}
.aw__cta-line { font-size: 1.05rem; line-height: 1.35; color: #fff; font-weight: 600; letter-spacing: -0.015em; }
.aw__cta-go { display: inline-flex; align-items: center; gap: 7px; font-size: .84rem; font-weight: 600; color: rgba(255,255,255,.76); }
.aw__cta-go svg { width: 15px; height: 15px; }
.aw__card--cta:hover .aw__cta-go { color: #fff; }
.aw__link2 { color: var(--muted); }

.aw__cta {
  position: relative; overflow: hidden;
  margin-top: clamp(24px, 3vw, 40px);
  padding: clamp(22px, 2.6vw, 30px) clamp(24px, 3vw, 36px);
  border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--paper);
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; flex-wrap: wrap;
}
.aw__cta::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 5px; background: var(--accent); }
.aw__cta h3 { font-size: clamp(1.2rem, 2vw, 1.55rem); }

@media (max-width: 1000px) {
  .aw__head { grid-template-columns: 1fr; align-items: start; }
  .aw__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) { .aw__grid { grid-template-columns: 1fr; } }
@media (prefers-reduced-motion: reduce) { .aw__card { transition: none; } .aw__card:hover { transform: none; } }

/* ============ INDUSTRY RECOGNITION (2026-09-29) ============
   Awards page, section 2 of the client's review. Reuses the .filters chips from
   the portfolio and the .aw__* card above it. */
/* Chips sit on their own row under the heading. Six of them in a right-hand
   column squeezed "Industry Recognition" into two narrow lines. */
.rec__head { margin-bottom: clamp(24px, 3vw, 38px); }
.rec__head .filters { margin: clamp(22px, 2.6vw, 30px) 0 0; justify-content: flex-start; }

.rec-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(14px, 1.6vw, 22px); }
@media (max-width: 1000px) { .rec-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px)  { .rec-grid { grid-template-columns: 1fr; } }

.rec {
  position: relative; display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--paper);
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease);
}
.rec:hover { border-color: #CBD5E1; box-shadow: var(--shadow); }
/* 3/2, not 4/3: seven of the eight have no certificate on file and a taller
   plate turns the grid into a wall of empty boxes. */
.rec__media { aspect-ratio: 3 / 2; background: var(--paper-3); display: block; overflow: hidden; }
/* display:block, not grid: with place-items:center the certificate stopped
   resolving its 100% height against the row and object-fit:contain had
   nothing to contain against, so the scan was cropped at the bottom. */
/* Project photographs fill the plate; documents are contained below. */
.rec__media img { width: 100%; height: 100%; object-fit: cover; }
/* No certificate scan on file for this award. A quiet plate says so honestly;
   a stock trophy photograph would not. */
.rec__media--type {
  display: grid; place-items: center;
  background:
    repeating-linear-gradient(135deg, rgba(30,32,34,.028) 0 12px, transparent 12px 24px),
    var(--paper-3);
}
.rec__seal img { width: 44px; height: auto; opacity: .26; padding: 0; }
.rec__body { display: flex; flex-direction: column; gap: 6px; padding: 16px 44px 18px 16px; flex: 1; }
.rec__body h3 { font-size: 1rem; line-height: 1.25; }
.rec__issuer {
  font-size: .68rem; font-weight: 700; letter-spacing: .11em; text-transform: uppercase;
  color: var(--muted); line-height: 1.45;
}
.rec__go {
  position: absolute; right: 12px; bottom: 14px;
  width: 30px; height: 30px; display: grid; place-items: center;
  color: var(--accent); transition: transform .22s var(--ease);
}
.rec__go svg { width: 16px; height: 16px; }
.rec:hover .rec__go { transform: translateX(3px); }
.rec.is-hidden { display: none; }
@media (prefers-reduced-motion: reduce) { .rec, .rec__go { transition: none; } }

/* ---- recognition tiles carrying the client's 2026-09-29 delivery ---- */
.rec__award {
  font-size: .67rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-ink); line-height: 1.4;
}
.rec__body h3 { font-size: .98rem; }
/* A certificate stands in when there is no project photograph. It is a document:
   contain it on paper, never crop it the way a photograph can be cropped. */
.rec__media--doc { background: var(--paper); }
/* A certificate is a document, not a photograph — contain it on paper,
   never crop it the way a photograph can be cropped. */
.rec__media--doc img { object-fit: contain; padding: 10px; }
.rec__chip {
  align-self: flex-start; margin-top: 2px;
  font-size: .64rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); background: var(--paper-3);
  border-radius: 999px; padding: 4px 9px;
}

/* ---- awards on a portfolio tile (2026-09-30) ----
   From the client's awards tracker. The caption carries the award itself; the
   marker on the photograph is so the wall reads at a glance which projects hold
   one. "Certified" and "Award" are kept apart — a LEED plaque and a design award
   are not the same claim. */
.proj__flag {
  position: absolute; top: 10px; left: 10px; z-index: 2;
  background: var(--accent); color: #fff;
  font-size: .6rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  padding: 5px 10px; border-radius: 999px;
}
.proj__awards { display: flex; flex-direction: column; gap: 4px; margin-top: 9px; }
.proj__award {
  font-size: .78rem; line-height: 1.4; font-weight: 600; color: var(--accent-ink);
}
a.proj__award {
  text-decoration: underline; text-decoration-color: rgba(196,2,2,.3);
  text-underline-offset: 3px; transition: text-decoration-color .2s var(--ease);
}
a.proj__award:hover { text-decoration-color: var(--accent); }

/* The hover card's award lines went with the card, 2026-09-30. */

/* Letter date on a reference card. The three we hold are from 2012 and the card
   says so rather than implying they are recent. */
.fb__card figcaption .fb__dated {
  display: block; margin-top: 6px; font-style: normal;
  font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--brand-grey);
}

/* ===== 2026-10-03 THUMBNAIL CAROUSEL ADDITION =====
   Client tweaks section 3: Portfolio and Awards thumbnails show several project
   photographs rather than one. Emitted only when a record holds more than one
   picture (see carousel() in build.py), so a single-photograph tile is untouched
   by everything below.

   The track is a scroll-snap row: swipe and trackpad work with no JavaScript,
   and main.js only adds the arrows, the dots and the counter. Sits inside the
   existing .proj__media / .rec__media plate and inherits its aspect-ratio, so
   the grid geometry does not move. */
.car { position: relative; width: 100%; height: 100%; overflow: hidden; }
.car__track {
  display: flex; list-style: none; margin: 0; padding: 0;
  width: 100%; height: 100%;
  overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: none; -ms-overflow-style: none;
  scroll-behavior: smooth;
}
.car__track::-webkit-scrollbar { display: none; }
.car__slide { flex: 0 0 100%; width: 100%; height: 100%; scroll-snap-align: center; }
.car__slide img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Controls stay hidden until the tile is hovered or something inside it is
   focused — on a grid of 90 tiles, permanent arrows are visual noise. They are
   always present for keyboard and touch users via :focus-visible below. */
.car__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 34px; height: 34px; border: 0; border-radius: 50%;
  background: rgba(30,32,34,.55); color: #fff; cursor: pointer;
  display: grid; place-items: center; padding: 0;
  opacity: 0; transition: opacity .22s var(--ease), background .22s var(--ease);
  z-index: 2; backdrop-filter: blur(3px);
}
.car__nav svg { width: 18px; height: 18px; }
.car__nav--prev { left: 8px; }
.car__nav--next { right: 8px; }
.car__nav:hover { background: var(--accent); }
.car__nav[hidden] { display: none; }
.proj:hover .car__nav,
.rec:hover .car__nav,
.car:focus-within .car__nav { opacity: 1; }
.car__nav:focus-visible { opacity: 1; outline: 2px solid #fff; outline-offset: 2px; }

.car__dots {
  position: absolute; left: 0; right: 0; bottom: 9px;
  display: flex; gap: 6px; justify-content: center; z-index: 2;
}
.car__dot {
  width: 6px; height: 6px; padding: 0; border: 0; border-radius: 50%;
  background: rgba(255,255,255,.55); cursor: pointer;
  transition: background .2s var(--ease), transform .2s var(--ease);
  box-shadow: 0 0 0 1px rgba(30,32,34,.25);
}
.car__dot[aria-current="true"] { background: #fff; transform: scale(1.5); }
.car__dot:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* Counter instead of dots once there are more than six photographs — eight dots
   at 6px read as dirt on the picture. */
.car__count {
  position: absolute; right: 9px; bottom: 8px; z-index: 2;
  font-size: .72rem; font-weight: 600; letter-spacing: .02em;
  color: #fff; background: rgba(30,32,34,.55); backdrop-filter: blur(3px);
  padding: 2px 7px; border-radius: 999px;
  opacity: 0; transition: opacity .22s var(--ease);
}
.car--many .car__dots { display: none; }
.car--many .car__count,
.proj:hover .car--many .car__count,
.rec:hover .car--many .car__count { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .car__track { scroll-behavior: auto; }
  .car__dot { transition: none; }
}
/* ===== END 2026-10-03 THUMBNAIL CAROUSEL ADDITION ===== */
