/* ===========================================================================
   COREN Design — shared stylesheet
   Built against the high-resolution homepage comp.

   Signature motif: concave "notch" corners. White panels tuck into the
   corners of photo cards and the surrounding fill curves in to meet them.
   It appears in the header, the hero panel, team cards, project cards and
   testimonial cards — implemented once as .fillet and reused throughout.
   =========================================================================== */

/* ── tokens ──────────────────────────────────────────── */
:root {
  /* brand — sampled from the supplied logo artwork */
  --blue:        #306BCF;
  --blue-dark:   #24519E;
  --blue-soft:   #EAF0FB;
  --orange:      #FEA544;
  --orange-deep: #EC8425;

  /* neutrals — sampled from the comp */
  --ink:        #0E1013;
  --ink-2:      #33383E;
  --gray:       #767C84;
  --gray-light: #A8ADB4;
  --rule:       #C9CBCE;
  --rule-soft:  #E1E3E5;
  --surface:    #E5E5E5;
  --panel-dark: #16181B;
  --white:      #FFFFFF;

  /* type — the comp shows a double-storey 'a' and a single-storey
     open-tail 'g', which rules out Poppins. Plus Jakarta Sans matches. */
  --font: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* geometry */
  --r-sm:  10px;
  --r-md:  16px;
  --r-lg:  22px;
  --r-pill: 999px;
  --notch: 26px;

  /* rhythm */
  --gutter:  clamp(16px, 4vw, 54px);
  --section: clamp(60px, 8vw, 112px);
  --maxw:    1260px;

  --shadow-sm: 0 2px 10px rgba(14, 16, 19, .07);
  --shadow-md: 0 10px 30px rgba(14, 16, 19, .11);
  --shadow-lg: 0 20px 54px rgba(14, 16, 19, .18);
}

/* ── reset ───────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, picture, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button, input { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
ul { list-style: none; }

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

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── layout ──────────────────────────────────────────── */
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
.section { padding-block: var(--section); }
.section--gray { background: var(--surface); }

/* ── type scale ──────────────────────────────────────── */
.h1 { font-size: clamp(2.4rem, 6vw, 4.4rem); line-height: 1.04; font-weight: 700; letter-spacing: -.025em; }
.h2 { font-size: clamp(1.95rem, 4.4vw, 3.35rem); line-height: 1.1; font-weight: 700; letter-spacing: -.022em; }
.h3 { font-size: clamp(1.1rem, 1.9vw, 1.45rem); line-height: 1.25; font-weight: 600; letter-spacing: -.012em; }

/* The comp justifies the supporting paragraph — kept, it's deliberate. */
.lede { color: var(--gray); font-size: .8rem; line-height: 1.7; text-align: justify; }

.eyebrow {
  display: inline-block;
  padding: 11px 26px;
  border-radius: var(--r-sm);
  background: var(--blue);
  color: var(--white);
  font-size: .9rem;
  font-weight: 500;
  /* Transparent border so the solid and glass variants measure identically —
     the glass one carries a visible 1px border. */
  border: 1px solid transparent;
}
.eyebrow--glass {
  background: rgba(38, 40, 44, .58);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, .42);
}

/* ── concave corner fillet ───────────────────────────────
   Paints the surrounding colour curving inward at a panel corner, so a
   white panel reads as carved into a photo rather than laid on top.
   --fill is the colour being curved in from.
─────────────────────────────────────────────────────── */
.fillet {
  position: absolute;
  width: var(--notch);
  height: var(--notch);
  overflow: hidden;
  pointer-events: none;
  z-index: 3;
}
.fillet::before {
  content: "";
  position: absolute;
  width: 200%; height: 200%;
  border-radius: 50%;
  box-shadow: 0 0 0 var(--notch) var(--fill, #fff);
}
.fillet--bl::before { right: 0; top: 0; }
.fillet--br::before { left: 0;  top: 0; }
.fillet--tl::before { right: 0; bottom: 0; }
.fillet--tr::before { left: 0;  bottom: 0; }

/* ── buttons ─────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 15px 30px;
  border-radius: var(--r-sm);
  font-size: .95rem; font-weight: 500; white-space: nowrap;
  transition: transform .18s ease, background .18s ease, color .18s ease, border-color .18s ease;
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }
.btn--blue { background: var(--blue); color: var(--white); }
.btn--blue:hover { background: var(--blue-dark); }
.btn--white { background: var(--white); color: var(--ink); }
.btn--white:hover { background: var(--blue); color: var(--white); }
.btn--dark { background: var(--panel-dark); color: var(--white); }
.btn--dark:hover { background: var(--blue); }
.btn--outline { background: transparent; color: var(--ink); border: 1.5px solid var(--ink); }
.btn--outline:hover { background: var(--ink); color: var(--white); }
.btn--pill { border-radius: var(--r-pill); }
.btn__arrow { font-size: 1.1em; line-height: 1; font-weight: 700; }

.nav-btn {
  width: 44px; height: 44px; flex: 0 0 44px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1.5px solid var(--ink);
  color: var(--ink); background: transparent;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.nav-btn:hover:not(:disabled) { background: var(--blue); border-color: var(--blue); color: var(--white); }
.nav-btn:disabled { opacity: .3; cursor: not-allowed; }
.nav-btn svg { width: 17px; height: 17px; }

.counter { font-size: 1rem; font-weight: 500; letter-spacing: .04em; }

.ph {
  background-color: #D5D9DE;
  background-image:
    linear-gradient(135deg, rgba(48,107,207,.15), rgba(236,132,37,.13)),
    linear-gradient(160deg, #C6CCD3 0%, #E2E5E9 55%, #D0D5DB 100%);
  background-size: cover; background-position: center;
}

/* ===========================================================================
   HEADER + HERO
   The comp shows the photo rising between two white plates: logo at
   top-left, nav at top-right.
   =========================================================================== */
.hero { position: relative; }
.hero__frame {
  position: relative;
  overflow: hidden;
  /* Full-bleed: edge to edge horizontally and the full viewport vertically,
     so the hero is all you see on arrival. svh tracks the small viewport on
     mobile so the frame doesn't jump as browser chrome hides; vh is the
     fallback for older engines. */
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
}
.hero__media { position: absolute; inset: 0; }
.hero__media img, .hero__media .ph { width: 100%; height: 100%; object-fit: cover; }
.hero__media picture { display: block; width: 100%; height: 100%; }

/* ── hero slideshow ──────────────────────────────────────
   Slides stack and crossfade. Add or remove .hero__slide elements in the
   markup and the script adapts — no config to keep in sync.
──────────────────────────────────────────────────────── */
.hero__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1.5s ease-in-out;
  will-change: opacity;
}
.hero__slide.is-active { opacity: 1; }
.hero__slide picture { display: block; width: 100%; height: 100%; }
.hero__slide img, .hero__slide .ph {
  width: 100%; height: 100%;
  object-fit: cover;
  /* Slow drift keeps a still photo from feeling frozen. */
  animation: heroDrift 14s ease-out forwards;
}
.hero__slide:not(.is-active) img, .hero__slide:not(.is-active) .ph { animation: none; }

@keyframes heroDrift {
  from { transform: scale(1); }
  to   { transform: scale(1.07); }
}

/* Optional progress dots — hidden by default, matching the comp. */
.hero__dots {
  position: absolute;
  left: 50%; bottom: 18px;
  transform: translateX(-50%);
  display: none;
  gap: 8px;
  z-index: 4;
}
.hero__dots.is-shown { display: flex; }
.hero__dots button {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: rgba(255,255,255,.45);
  transition: background .25s ease, width .25s ease;
}
.hero__dots button.is-active { background: var(--white); width: 22px; border-radius: 4px; }
/* Scrim. Two layers: a wedge from the left that guarantees the headline
   reads whatever slide is behind it, and a bottom fade that seats the white
   panel. Tuned by measuring worst-case contrast across all four slides —
   the weakest lands at 4.0:1, clearing the 3:1 AA bar for large text with
   margin, while leaving the right and top of the photo unmuted. */
.hero__frame::after {
  content: "";
  position: absolute; inset: 0;
  background:
    linear-gradient(to right, rgba(14,16,19,.62) 0%, rgba(14,16,19,.14) 60%, rgba(14,16,19,0) 82%),
    linear-gradient(180deg, rgba(14,16,19,0) 38%, rgba(14,16,19,.42) 64%, rgba(14,16,19,.78) 100%);
}

.site-header { position: absolute; top: 0; left: 0; right: 0; z-index: 40; }
/* Full-bleed hero behind, so the plates reach the screen edges. Their inner
   padding lines the logo and nav up with the page container. */
.site-header .wrap { max-width: none; padding-inline: 0; }
/* Stretch so both plates share a height — the nav plate is taller because it
   holds the Contact button, and left to itself the pair looked misaligned. */
.header-inner { display: flex; align-items: stretch; justify-content: space-between; gap: 20px; }

.header-plate { background: var(--white); display: flex; align-items: center; position: relative; }
.header-plate--logo { padding: 14px clamp(22px, 3vw, 34px) 18px clamp(16px, 4vw, 54px); border-bottom-right-radius: var(--r-lg); }
.header-plate--nav { padding: 14px clamp(16px, 4vw, 54px) 18px clamp(22px, 3vw, 34px); border-bottom-left-radius: var(--r-lg); gap: 34px; }

.logo { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.logo__mark { width: 50px; height: auto; flex: 0 0 50px; }
.logo__text { display: flex; flex-direction: column; line-height: 1; }
.logo__name { font-size: 1.42rem; font-weight: 800; letter-spacing: .005em; color: var(--blue); }
.logo__sub { font-size: .43rem; font-weight: 600; letter-spacing: .21em; color: var(--blue); text-transform: uppercase; margin-top: 3px; }

.site-nav ul { display: flex; align-items: center; gap: 36px; }
.site-nav a { font-size: .97rem; font-weight: 400; color: var(--ink-2); padding-block: 5px; position: relative; transition: color .18s ease; white-space: nowrap; }

/* Five nav items plus the CTA no longer fit at 36px spacing once the header
   drops below about 1080px — "About us" and "Our Services" broke onto two
   lines. Tighten the gap before the mobile breakpoint takes over at 880px. */
@media (max-width: 1080px) {
  .site-nav ul { gap: 22px; }
  .site-nav a { font-size: .9rem; }
}
@media (max-width: 960px) {
  .site-nav ul { gap: 16px; }
  .site-nav a { font-size: .86rem; }
}
.site-nav a:hover { color: var(--blue); }
.site-nav a[aria-current="page"] { font-weight: 700; color: var(--ink); }
.site-nav a[aria-current="page"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -3px;
  height: 2.5px; border-radius: 2px; background: var(--blue);
}
/* Only ever shown inside the mobile dropdown — see the 880px block, where
   .header-cta is hidden and this takes over. On desktop the header button
   already covers it, so a duplicate in the nav would be noise. */
.nav-cta { display: none; }

/* ── booking CTA: hazard tape ─────────────────────────
   The site's primary action, dressed as barricade tape. A strip of caution
   tape runs along the button's lower lip at rest; on hover the tape starts
   marching sideways, a faint diagonal weave appears across the whole face,
   and a glint sweeps over it.

   The lip is a background-image on the button itself rather than a
   pseudo-element, so the marching animation is a single background-position
   shift. .btn--blue:hover uses the `background` shorthand, which would reset
   that image to none — hence the doubled-up selectors below setting
   background-color only.

   The full-face weave is white at 16%, not orange. Orange at any usable
   opacity over the dark blue turns grey and muddy, and stripes with real
   contrast under white text destroy the legibility that makes this button
   work in the first place. */
/* ── CTA glow ─────────────────────────────────────────
   A slow pulse every three seconds on the buttons that lead to the booking
   widget, so they read as the live action on the page rather than sitting
   there inert.

   The glow is a box-shadow, animated from transparent out to a coloured
   halo and back inside the first quarter of the cycle — the remaining two
   seconds are still. Each button sets its own --glow-on, because a blue
   halo is invisible against the blue header button and a white halo is
   invisible against the white quote pill.

   Buttons opt in individually. Every button on the page pulsing at once
   would be noise, and would tell a visitor nothing about where to click. */
@keyframes cta-glow {
  0%   { box-shadow: 0 0 0 0 var(--glow-off, transparent); }
  7%   { box-shadow: 0 0 24px 5px var(--glow-on); }
  24%  { box-shadow: 0 0 0 0 var(--glow-off, transparent); }
  100% { box-shadow: 0 0 0 0 var(--glow-off, transparent); }
}

.btn--outline {
  --glow-on: rgba(48, 107, 207, .55);
  --glow-off: rgba(48, 107, 207, 0);
  animation: cta-glow 3s ease-in-out infinite;
}
.btn--quote {
  /* White on a photograph — a brand-blue halo would disappear into the
     darkened background behind it. */
  --glow-on: rgba(255, 255, 255, .8);
  --glow-off: rgba(255, 255, 255, 0);
  animation: cta-glow 3s ease-in-out infinite;
}

.btn--blue.header-cta,
.nav-cta a {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background-image: repeating-linear-gradient(-45deg,
    var(--orange) 0 10px, transparent 10px 20px);
  background-repeat: repeat-x;
  background-size: 28.3px 6px;   /* 28.3px is one full diagonal period, so it tiles seamlessly */
  background-position: 0 100%;
  /* Two independent loops on one element: the tape pulls every five seconds,
     the halo pulses every three. They animate different properties, so they
     never fight. */
  --glow-on: rgba(48, 107, 207, .6);
  --glow-off: rgba(48, 107, 207, 0);
  animation: tape-idle 5s linear infinite,
             cta-glow 3s ease-in-out infinite;
}

/* Moves for the first fifth of the cycle, then holds still for the rest.
   The travel is three whole tiles, so the jump back to the start at 100% is
   invisible — the pattern is already identical at that offset. */
@keyframes tape-idle {
  0%   { background-position: 0 100%; }
  22%  { background-position: 84.9px 100%; }
  100% { background-position: 84.9px 100%; }
}

.btn--blue.header-cta:hover,
.btn--blue.header-cta:focus-visible,
.nav-cta a:hover,
.nav-cta a:focus-visible {
  background-color: var(--blue-dark);
  animation: tape-march 1.15s linear infinite;
}

@keyframes tape-march {
  to { background-position: 28.3px 100%; }
}

/* Faint diagonal weave across the face, revealed on hover. */
.header-cta::before,
.nav-cta a::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: repeating-linear-gradient(-45deg,
    rgba(255,255,255,.55) 0 10px, transparent 10px 20px);
  background-size: 28.3px 28.3px;
  opacity: 0;
  transition: opacity .3s ease;
}
.header-cta:hover::before,
.header-cta:focus-visible::before,
.nav-cta a:hover::before,
.nav-cta a:focus-visible::before {
  opacity: .16;
  animation: tape-march-face 1.15s linear infinite;
}
@keyframes tape-march-face {
  to { background-position: 28.3px 0; }
}

/* Surveyor's-tape glint, sweeping once on hover. */
.header-cta::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(100deg, transparent 42%, rgba(255,255,255,.28) 50%, transparent 58%);
  transform: translateX(-125%);
  transition: transform .6s cubic-bezier(.3, .7, .3, 1);
}
.header-cta:hover::after,
.header-cta:focus-visible::after { transform: translateX(125%); }

@media (prefers-reduced-motion: reduce) {
  /* The tape stays — it carries the meaning — but nothing moves, including
     the five-second idle pull and the three-second glow. */
  .btn--blue.header-cta,
  .nav-cta a,
  .btn--blue.header-cta:hover,
  .btn--blue.header-cta:focus-visible,
  .nav-cta a:hover,
  .nav-cta a:focus-visible,
  .btn--outline,
  .btn--quote { animation: none; }
  .header-cta:hover::before,
  .nav-cta a:hover::before { animation: none; }
  .header-cta::after { display: none; }
}

.header-cta { padding: 13px 34px; font-size: .95rem; white-space: nowrap; }

/* "Book a Consultation" is roughly 90px wider than the "Contact" this
   replaced, which was enough to push "About us" and "Our Services" onto two
   lines between here and the mobile breakpoint. Tighter padding buys the
   room back without shrinking the tap target's height. */
@media (max-width: 1080px) {
  .header-cta { padding-inline: 20px; font-size: .88rem; }
}

.menu-toggle { display: none; width: 44px; height: 44px; place-items: center; border-radius: var(--r-sm); }
.menu-toggle span { display: block; width: 20px; height: 2px; background: var(--ink); border-radius: 2px; transition: transform .22s ease, opacity .22s ease; }
.menu-toggle span + span { margin-top: 5px; }
.menu-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.menu-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.menu-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.hero__body { position: relative; z-index: 2; width: 100%; padding-block: clamp(26px, 4vw, 54px) clamp(70px, 9vw, 110px); }
/* Scaled up for the full-height frame — at the old size the headline sat
   small against a viewport-tall photo. Overrides the shared .h1 ceiling
   without affecting the About page hero. */
.hero__title {
  color: var(--white);
  max-width: 14ch;
  font-size: clamp(2.4rem, 6.4vw, 5.1rem);
  margin-bottom: clamp(30px, 4vw, 52px);
  text-shadow: 0 2px 18px rgba(14, 16, 19, .38);
}

/* The panel straddles the boundary between the hero and the section below.
   It lives outside the frame because the frame clips its overflow, which cut
   the panel flat at the seam. Half above, half below, fully rounded. */
.hero__panel-rail {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  transform: translateY(50%);
  z-index: 6;
  pointer-events: none;
}
.hero__panel-rail .wrap { pointer-events: none; }
.hero__panel {
  pointer-events: auto;
  display: inline-flex; align-items: center; flex-wrap: wrap;
  gap: clamp(18px, 3vw, 44px);
  background: var(--white);
  border-radius: var(--r-lg);
  padding: 22px 22px 22px 30px;
  box-shadow: var(--shadow-lg);
}
.hero__panel p { font-size: clamp(1rem, 1.5vw, 1.22rem); font-weight: 700; line-height: 1.42; max-width: 20ch; }
.hero__panel .accent { color: var(--blue); }

/* A full-height hero hides the fact that anything follows it, so this gives
   the eye somewhere to go. Sits opposite the panel to avoid collision. */
.hero__scroll {
  position: absolute;
  right: clamp(22px, 3.5vw, 48px);
  bottom: clamp(26px, 4vw, 50px);
  z-index: 4;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  color: rgba(255, 255, 255, .9);
  font-size: .68rem;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  writing-mode: vertical-rl;
  transition: color .2s ease;
}
.hero__scroll:hover { color: var(--white); }
.hero__scroll span {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, .55);
  writing-mode: horizontal-tb;
  animation: scrollNudge 2.2s ease-in-out infinite;
}
.hero__scroll:hover span { border-color: var(--white); }
.hero__scroll svg { width: 15px; height: 15px; }

@keyframes scrollNudge {
  0%, 100% { transform: translateY(0); opacity: .85; }
  50%      { transform: translateY(6px); opacity: 1; }
}

/* ===========================================================================
   SERVICES
   =========================================================================== */
.services { position: relative; padding-top: calc(var(--section) + clamp(30px, 4vw, 56px)); padding-bottom: var(--section); overflow: hidden; isolation: isolate; }
/* Background is pre-blurred as a build step — no CSS filter, so there's no
   per-frame GPU cost and the asset compresses to a fraction of the size. */
.services__bg { position: absolute; inset: 0; z-index: -2; }
.services__bg img, .services__bg .ph { width: 100%; height: 100%; object-fit: cover; }
.services__bg picture { display: block; width: 100%; height: 100%; }
.services::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(26,28,32,.60), rgba(26,28,32,.52) 45%, rgba(26,28,32,.62));
}
.services__head { text-align: center; margin-bottom: clamp(30px, 4vw, 46px); }
.services__head .eyebrow { margin-bottom: 24px; }
/* Held to a single line. The string measures 13.67em in Plus Jakarta Sans 600
   once the -.022em tracking is applied, so the width that fits is ~6.4vw at
   every breakpoint; 6vw leaves margin. The rem cap keeps it from growing
   past the intended size on wide screens. */
.services__title {
  color: var(--white);
  margin-inline: auto;
  font-weight: 700;
  font-size: min(3.35rem, 6vw);
  white-space: nowrap;
}

/* Cards in a 2x2 grid. Each crossfades on hover to reveal a project photo,
   so the container only sets size — the two faces carry the styling. */
.services__grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(14px, 1.8vw, 22px);
  max-width: 1020px;
  margin-inline: auto;
}

.svc {
  position: relative;
  height: clamp(148px, 15vw, 184px);
}
.svc__inner { position: absolute; inset: 0; }

.svc__face {
  position: absolute; inset: 0;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1.5px solid rgba(255, 255, 255, .58);
  color: var(--white);
  /* Both faces are always stacked and both are painted; opacity alone
     decides which one is seen. Nothing rotates, so nothing has to be
     hidden by backface-visibility. */
  transition: opacity .26s ease;
}

.svc__face--front {
  display: flex; align-items: center; gap: 18px;
  padding: 22px 26px;
  background: rgba(46, 48, 52, .46);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  z-index: 2;
  opacity: 1;
}
.svc:hover .svc__face--front,
.svc:focus-within .svc__face--front { opacity: 0; }

/* Icon gives the card a fixed anchor, so labels of different lengths still
   sit on a common baseline across the grid. */
.svc__icon {
  flex: 0 0 46px;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, .5);
  background: rgba(255, 255, 255, .10);
}
.svc__icon svg { width: 22px; height: 22px; }
.svc__label { font-size: clamp(.95rem, 1.25vw, 1.1rem); font-weight: 500; line-height: 1.35; }

/* Back face: photo with the service name over a gradient so it stays legible.
   It has to be fully hidden at rest, not merely underneath — the front is
   translucent frosted glass, so anything stacked below it shows through and
   the photo and caption ghosted into the resting state.
   It fades in faster than the front fades out, so the two overlap and the
   section background never flashes between them. */
.svc__face--back {
  display: flex; align-items: flex-end;
  z-index: 1;
  opacity: 0;
  transition: opacity .17s ease;
}
.svc:hover .svc__face--back,
.svc:focus-within .svc__face--back { opacity: 1; }
.svc__face--back img, .svc__face--back .ph {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.svc__face--back::after {
  content: "";
  position: absolute; inset: 0;
  /* Strong enough to carry the caption over a light image — the structural
     render sits on white, where the previous stops left text at 3.6:1. */
  background: linear-gradient(180deg, rgba(14,16,19,.10) 0%, rgba(14,16,19,.24) 45%, rgba(14,16,19,.94) 100%);
}
.svc__caption {
  position: relative; z-index: 2;
  padding: 18px 22px;
  font-size: clamp(.9rem, 1.15vw, 1rem);
  font-weight: 600;
  line-height: 1.3;
  text-shadow: 0 1px 10px rgba(14,16,19,.5);
}

/* No hover on touch, so the back face would be unreachable. Those visitors
   get the photo as a permanent backdrop behind the label instead. */
@media (hover: none) {
  .svc { height: clamp(132px, 34vw, 168px); }
  .svc__face--back { display: none; }
  .svc__face--front { background: rgba(46, 48, 52, .52); opacity: 1; }
}

.services__foot { text-align: center; margin-top: clamp(28px, 3.6vw, 44px); }

/* ===========================================================================
   TEAM
   =========================================================================== */
.team__title { text-align: center; max-width: 16ch; margin: 0 auto clamp(34px, 4.4vw, 54px); }
.team__viewport { overflow: hidden; padding-block: 26px; }
.team__track { display: flex; gap: clamp(14px, 1.8vw, 22px); align-items: center; transition: transform .5s cubic-bezier(.22,.61,.36,1); }
/* With nothing to scroll through, centre the lone card instead of pinning it
   left against empty space. */
.team__track.is-single { justify-content: center; }
.team__track.is-single .member { transform: none; }
.team__track.is-single .member.is-active { transform: none; box-shadow: var(--shadow-md); }

.member {
  flex: 0 0 clamp(196px, 22vw, 262px);
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  aspect-ratio: 5 / 7;
  background: linear-gradient(180deg, var(--orange) 0%, var(--orange-deep) 100%);
  transition: transform .4s ease, box-shadow .4s ease;
}
.member__photo { position: absolute; inset: 0; }
.member__photo picture { display: block; width: 100%; height: 100%; }
.member__photo img, .member__photo .ph { width: 100%; height: 100%; object-fit: cover; object-position: top center; }

.member.is-active {
  background: linear-gradient(180deg, #86B6E8 0%, #5C93D6 100%);
  transform: scale(1.13);
  box-shadow: var(--shadow-lg);
  z-index: 2;
}

/* Info bar tucks into the bottom-right; card curves in to meet it. */
.member__info {
  position: absolute;
  left: 13%; right: 0; bottom: 0;
  background: var(--white);
  padding: 13px 16px 14px;
  border-top-left-radius: var(--r-md);
  z-index: 2;
}
.member__name { font-size: .74rem; font-weight: 800; letter-spacing: .02em; text-transform: uppercase; line-height: 1.3; }
.member__role { font-size: .63rem; color: var(--gray); letter-spacing: .05em; text-transform: uppercase; }

.team__controls { display: flex; align-items: center; justify-content: center; gap: 20px; margin-top: clamp(24px, 3vw, 36px); }

/* ===========================================================================
   PROJECTS
   =========================================================================== */
.rule { border: 0; border-top: 1px solid var(--rule); }
/* Section labels sit centred in every section, matching "Our Services". The
   heading and supporting column below stay left-aligned. */
.projects__head .eyebrow {
  display: block;
  width: fit-content;
  margin-inline: auto;
  margin-bottom: clamp(18px, 2.4vw, 26px);
}
/* Both headline rows share a ratio and vertically centre their supporting
   column against the heading, so a three-line heading doesn't leave the
   paragraph stranded at the top. */
.projects__headline { display: grid; grid-template-columns: 1.4fr 1fr; gap: clamp(24px, 6vw, 90px); align-items: center; margin-block: clamp(24px, 3vw, 36px); }
/* Shared measure across both headline rows: 18ch is the point where each
   settles into three lines rather than four. */
.projects__headline .h2 { max-width: 18ch; }
.projects__bar { display: flex; align-items: center; gap: 16px; margin-top: clamp(20px, 2.6vw, 30px); margin-bottom: clamp(26px, 3.4vw, 40px); }

.projects__viewport { overflow: hidden; }
.projects__track { display: flex; gap: clamp(14px, 1.8vw, 20px); transition: transform .5s cubic-bezier(.22,.61,.36,1); }

/* Each card is a single link to the project's Facebook album. The visible
   button is a span, not a nested anchor, so the whole card is one target —
   valid markup, one tab stop, and the button still reads as the affordance. */
.project {
  flex: 0 0 clamp(290px, 46%, 540px);
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  aspect-ratio: 4 / 3.6;
  display: flex;
  align-items: flex-end;
  color: var(--white);
  transition: transform .28s ease, box-shadow .28s ease;
}
.project:hover { transform: translateY(-5px); box-shadow: var(--shadow-lg); }
.project:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; }

/* Photo drifts in slightly, so the whole card clearly responds to hover
   rather than only the button. */
.project__media img, .project__media .ph { transition: transform .5s ease; }
.project:hover .project__media img, .project:hover .project__media .ph { transform: scale(1.05); }

.project__cta { margin-top: 4px; }
.project:hover .project__cta { background: var(--ink); color: var(--white); }
.btn__fb { width: 15px; height: 15px; flex: 0 0 15px; }
.project__media { position: absolute; inset: 0; }
.project__media img, .project__media .ph { width: 100%; height: 100%; object-fit: cover; }
/* Tuned against real project photography, not the grey placeholders. The
   apartment facade puts a bright white gate directly under the caption,
   where the old gradient left text at 2:1. These stops reach 5.2:1 — clear
   of the 4.5 bar the small location line needs — without burying the photo. */
.project::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(14,16,19,0) 25%,
    rgba(14,16,19,.62) 68%,
    rgba(14,16,19,.88) 100%);
}
.project__body { position: relative; z-index: 2; width: 100%; padding: clamp(16px, 2vw, 24px); color: var(--white); }
.project__title { font-size: clamp(1.2rem, 2vw, 1.72rem); font-weight: 600; line-height: 1.15; margin-bottom: 12px; }
.project__title--rule { padding-bottom: 10px; border-bottom: 1.5px solid rgba(255,255,255,.7); display: inline-block; }
.project__loc { display: flex; align-items: flex-start; gap: 8px; font-size: .82rem; line-height: 1.45; margin-bottom: 16px; }
.project__loc svg { width: 14px; height: 14px; flex: 0 0 14px; margin-top: 3px; }
.project .btn { padding: 13px 24px; font-size: .88rem; }


/* ===========================================================================
   CTA
   =========================================================================== */
.cta { position: relative; padding-block: var(--section); overflow: hidden; isolation: isolate; text-align: center; }
.cta__bg { position: absolute; inset: 0; z-index: -2; }
.cta__bg img, .cta__bg .ph { width: 100%; height: 100%; object-fit: cover; }
.cta__bg picture { display: block; width: 100%; height: 100%; }
/* Tuned against the steel-frame photograph: the crop keeps the trusses, which
   puts bright sky behind the headline, so the wash is heavier than it was over
   a placeholder. Headline lands at 4.6:1 against a 3:1 requirement. */
.cta::before { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(180deg, rgba(14,16,19,.50), rgba(14,16,19,.60)); }
.cta__title {
  color: var(--white);
  max-width: 19ch;
  margin: 0 auto clamp(24px, 3vw, 36px);
  font-weight: 700;
  text-shadow: 0 2px 18px rgba(14, 16, 19, .34);
}

/* Lead capture: name, phone, email in a row, stacking on narrow screens. */
.lead { max-width: 880px; margin-inline: auto; }
.lead__fields { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; }
.lead input {
  flex: 1 1 200px;
  min-width: 0;
  padding: 17px 24px;
  border-radius: var(--r-pill);
  border: 1.5px solid transparent;
  background: var(--white);
  font-size: .95rem;
  transition: border-color .18s ease;
}
.lead input::placeholder { color: var(--gray-light); }
.lead input:focus-visible { outline: none; border-color: var(--blue); }
.lead input[aria-invalid="true"] { border-color: #D9534F; }

/* Off-screen rather than display:none — some bots skip hidden fields. */
.lead__trap { position: absolute !important; left: -9999px; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

.lead button {
  width: 54px; height: 54px; flex: 0 0 54px;
  display: grid; place-items: center;
  border-radius: var(--r-md);
  background: var(--white); color: var(--ink);
  transition: background .18s ease, color .18s ease, transform .18s ease, opacity .18s ease;
}
.lead button:hover:not(:disabled) { background: var(--blue); color: var(--white); transform: translateY(-2px); }
.lead button:disabled { opacity: .55; cursor: progress; }
.lead button svg { width: 20px; height: 20px; }
.subscribe__note { margin-top: 16px; font-size: .8rem; color: rgba(255,255,255,.88); }

/* ===========================================================================
   DOCUMENT PAGES — terms, privacy, cookies, help, faq
   =========================================================================== */
.doc { max-width: 800px; margin-inline: auto; }
.doc__meta {
  font-size: .76rem; color: var(--gray);
  padding-bottom: 16px; margin-bottom: clamp(28px, 3.4vw, 44px);
  border-bottom: 1px solid var(--rule-soft);
}
.doc h2 { font-size: clamp(1.08rem, 1.7vw, 1.38rem); font-weight: 700; letter-spacing: -.015em; margin-top: clamp(34px, 4vw, 52px); }
.doc > h2:first-child { margin-top: 0; }
.doc h3 { font-size: clamp(.94rem, 1.25vw, 1.06rem); font-weight: 700; margin-top: 26px; }
.doc p { font-size: .85rem; line-height: 1.78; color: var(--ink-2); margin-top: 14px; }
.doc ul { margin-top: 14px; padding-left: 20px; list-style: disc; }
.doc li { font-size: .85rem; line-height: 1.78; color: var(--ink-2); }
.doc li + li { margin-top: 8px; }
.doc strong { color: var(--ink); font-weight: 700; }
.doc a { color: var(--blue); text-decoration: underline; text-underline-offset: 3px; }
.doc a:hover { color: var(--blue-dark); }

.doc__note {
  margin-top: clamp(36px, 4vw, 54px);
  padding: clamp(18px, 2.2vw, 26px);
  border-radius: var(--r-md);
  background: var(--blue-soft);
}
/* The partners rail already carries its own bottom margin; stacking the
   note's on top of it would double the gap. */
.partners + .doc .doc__note,
.partners ~ .doc > .doc__note:first-child { margin-top: 0; }
.doc__note p:first-child { margin-top: 0; }

/* Help centre link cards. The whole card is the link, so it has to opt out of
   the .doc anchor styling — otherwise the heading and body copy inside it are
   rendered blue and underlined. */
.help-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(14px, 1.8vw, 22px); margin-top: clamp(22px, 2.6vw, 30px); }
.doc a.help-card,
.help-card {
  display: block;
  padding: clamp(20px, 2.4vw, 28px);
  border-radius: var(--r-md);
  background: var(--surface);
  color: inherit;
  text-decoration: none;
  transition: transform .22s ease, box-shadow .22s ease;
}
.help-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.help-card h3 { margin-top: 0; font-size: 1rem; font-weight: 700; color: var(--ink); }
.help-card:hover h3 { color: var(--blue); }
.help-card p { font-size: .78rem; margin-top: 8px; color: var(--ink-2); }
@media (max-width: 780px) { .help-grid { grid-template-columns: 1fr; } }

/* FAQ accordion — <details>, so it works with JavaScript disabled. */
.faq-list { max-width: 860px; margin-inline: auto; }
.faq-item { border-bottom: 1px solid var(--rule-soft); }
.faq-item summary {
  list-style: none;
  cursor: pointer;
  display: flex; align-items: flex-start; justify-content: space-between; gap: 18px;
  padding: clamp(17px, 2.1vw, 23px) 0;
  font-size: clamp(.9rem, 1.2vw, 1.02rem);
  font-weight: 600; line-height: 1.4;
  color: var(--ink);
  transition: color .18s ease;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:hover { color: var(--blue); }
.faq-item summary:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; border-radius: var(--r-sm); }
.faq-item summary svg {
  flex: 0 0 20px; width: 20px; height: 20px; margin-top: 2px;
  color: var(--blue);
  transition: transform .22s ease;
}
.faq-item[open] summary svg { transform: rotate(45deg); }
.faq-item__body { padding-bottom: clamp(18px, 2.2vw, 24px); }
.faq-item__body p { font-size: .85rem; line-height: 1.78; color: var(--ink-2); margin-top: 0; }
.faq-item__body p + p { margin-top: 12px; }
.faq-item__body a { color: var(--blue); text-decoration: underline; text-underline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .help-card:hover { transform: none; }
  .faq-item summary svg { transition: none; }
}

/* ── partners ─────────────────────────────────────────
   No logos were supplied at first, so the cards were built to work without
   them; all five have artwork now, and the layout holds either way.

   Flex, not grid. The first version put the discipline and the name in
   separate grid rows, and because the logo tile spans both rows its 88px
   height was distributed between them — the two lines ended up 42px apart
   with the label stranded at the top of the card. They are one block now,
   centred as a unit against the photo. */
.partners {
  /* Matches .doc so the cards line up with the paragraphs above and the note
     below. At 900 they overhung the text column by 50px each side, which read
     as the copy being indented rather than the cards being wide. */
  max-width: 800px;
  margin-inline: auto;
  margin-block: clamp(30px, 3.6vw, 48px);
  display: grid;
  gap: clamp(12px, 1.5vw, 18px);
}

.partner {
  position: relative;
  display: flex;
  align-items: center;
  gap: clamp(16px, 2.2vw, 28px);
  padding: clamp(18px, 2.2vw, 26px) clamp(20px, 2.8vw, 32px);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink);
  overflow: hidden;
  transition: transform .24s ease, box-shadow .24s ease, background .24s ease;
}
/* A rule of brand orange that grows down the leading edge on hover. */
.partner::before {
  content: "";
  position: absolute;
  left: 0; top: 50%; bottom: 50%;
  width: 4px;
  background: var(--orange);
  transition: top .28s ease, bottom .28s ease;
}
.partner:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); background: var(--white); }
.partner:hover::before { top: 0; bottom: 0; }
.partner:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }

.partner__num {
  flex: 0 0 auto;
  align-self: center;
  font-size: .72rem; font-weight: 700; color: var(--blue);
  font-variant-numeric: tabular-nums;
}

/* Fixed box, not a fixed width. The supplied marks range from a 4.6:1
   wordmark to a 0.7:1 badge; sizing on width alone would render the tall one
   three times the height of the card. Both are contained instead, so every
   tile is the same size whatever shape goes in it. */
.partner__logo {
  flex: 0 0 auto;
  position: relative;
  width: clamp(132px, 15vw, 172px);
  height: clamp(64px, 7.4vw, 88px);
  border-radius: var(--r-sm);
  /* The supplied artwork is on an opaque background, so it sits on its own
     tile rather than on the card — otherwise it renders as a rectangle with
     a hard edge. The hairline keeps the tile readable once the card itself
     turns white on hover. */
  background: var(--white);
  border: 1px solid var(--rule-soft);
}
/* Artwork drawn on a black field. On the white tile it read as a black
   rectangle with a hard edge — the same failure the white tile exists to
   prevent for logos on white. The tile matches the artwork instead, so the
   letterboxing disappears into it. */
.partner__logo--dark {
  background: #000;
  border-color: rgba(255, 255, 255, .16);
}
/* Absolute against the tile, so the logo's height resolves against a box the
   logo isn't helping to size. max-height:100% on a centred item resolves
   circularly and is ignored — the 0.7:1 badge rendered 244px tall inside an
   88px tile. Explicit width and height, not four insets: on a replaced
   element `auto` means the image's intrinsic size, so right and bottom are
   simply dropped and the logo spills out regardless. */
.partner__logo img {
  position: absolute;
  top: 10px; left: 12px;
  width: calc(100% - 24px);
  height: calc(100% - 20px);
  object-fit: contain;
  object-position: center;
}

.partner__text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.partner__discipline {
  font-size: .68rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--gray);
  line-height: 1.2;
}
.partner__name {
  font-size: clamp(1rem, 1.6vw, 1.24rem); font-weight: 700;
  letter-spacing: -.015em; line-height: 1.25;
}

.partner__link {
  flex: 0 0 auto;
  align-self: center;
  display: inline-flex; align-items: center; gap: 7px;
  font-size: .78rem; font-weight: 600; color: var(--blue);
  white-space: nowrap;
}
.partner__link svg { width: 14px; height: 14px; transition: transform .24s ease; }
.partner:hover .partner__link svg { transform: translate(2px, -2px); }

@media (max-width: 720px) {
  /* Not enough width for the logo, the name and the link on one line. The
     number keeps its own column down the left so the card still reads as
     part of a numbered list; everything else stacks against it. */
  .partner {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: start;
    gap: 12px 16px;
  }
  .partner__num { grid-column: 1; grid-row: 1 / span 3; padding-top: 2px; align-self: start; }
  .partner__logo { grid-column: 2; grid-row: 1; width: 148px; height: 76px; }
  .partner__text { grid-column: 2; grid-row: 2; }
  .partner__link { grid-column: 2; grid-row: 3; align-self: start; }
  /* Without a logo there is no row 1 to occupy, so the text leads. */
  .partner:not(.partner--logo) .partner__text { grid-row: 1; }
  .partner:not(.partner--logo) .partner__link { grid-row: 2; }
  .partner:not(.partner--logo) .partner__num { grid-row: 1 / span 2; }
}

@media (prefers-reduced-motion: reduce) {
  .partner:hover { transform: none; }
  .partner:hover .partner__link svg { transform: none; }
}

/* ===========================================================================
   FOOTER
   =========================================================================== */
.site-footer { padding-top: clamp(48px, 6.5vw, 82px); padding-bottom: 24px; }
.footer__grid { display: grid; grid-template-columns: minmax(0, 1.75fr) repeat(3, minmax(0, .75fr)); gap: clamp(26px, 4vw, 54px); padding-bottom: clamp(32px, 4.4vw, 50px); }

/* Footer lockup matches the header: full-colour mark, blue wordmark. */
.footer__about .logo__mark { width: 46px; flex-basis: 46px; }
.footer__about .logo { margin-bottom: 18px; }
.footer__about p { font-size: .76rem; line-height: 1.72; color: var(--ink-2); max-width: 44ch; }
.footer__about p strong { font-weight: 800; }
.footer__loc { display: flex; align-items: center; gap: 8px; margin-top: 16px; font-size: .78rem; }
.footer__loc svg { width: 14px; height: 14px; flex: 0 0 14px; }

.footer__col h4 { font-size: .86rem; font-weight: 700; margin-bottom: 16px; }
.footer__col li + li { margin-top: 11px; }
.footer__col a { font-size: .78rem; color: var(--ink-2); transition: color .18s ease; }
.footer__col a:hover { color: var(--blue); }

.footer__bottom { display: flex; align-items: center; justify-content: space-between; gap: 18px; flex-wrap: wrap; padding-top: 22px; border-top: 1px solid var(--rule-soft); }
.footer__bottom p { font-size: .76rem; color: var(--ink-2); }

/* Developer credit. Sits between the copyright and the socials on the bottom
   row; the logo is a transparent PNG so it reads on the white footer without
   a visible tile. Height is fixed and width auto so the lockup keeps its
   proportions whatever the source file is replaced with. */
.footer__dev { display: inline-flex; align-items: center; gap: 9px; font-size: .72rem; color: var(--gray); transition: opacity .18s ease; }
.footer__dev:hover { opacity: .68; }
.footer__dev img { width: auto; height: 22px; }
.socials { display: flex; gap: 18px; }
.socials a { color: var(--ink); transition: color .18s ease, transform .18s ease; }
.socials a:hover { color: var(--blue); transform: translateY(-2px); }
.socials svg { width: 18px; height: 18px; }

/* ===========================================================================
   RESPONSIVE
   =========================================================================== */
@media (max-width: 1080px) {
  .footer__grid { grid-template-columns: 1fr 1fr; }
  .footer__about { grid-column: 1 / -1; }
}

@media (max-width: 880px) {
  .menu-toggle { display: grid; }
  .header-plate--nav { padding: 10px clamp(16px, 4vw, 54px) 14px 20px; }
  .header-cta { display: none; }

  .site-nav {
    position: absolute; top: calc(100% + 8px); right: clamp(16px, 4vw, 54px);
    min-width: 220px;
    background: var(--white);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-md);
    padding: 10px; display: none;
  }
  .site-nav.is-open { display: block; }
  .site-nav ul { flex-direction: column; align-items: stretch; gap: 0; }
  .site-nav a { display: block; padding: 13px 16px; border-radius: var(--r-sm); }
  .site-nav a:hover { background: var(--blue-soft); }
  .site-nav a[aria-current="page"]::after { display: none; }

  /* The header button is hidden at this width, so without this there is no
     route to the booking widget from the header on a phone at all. */
  .nav-cta { display: block; margin-top: 6px; padding-top: 8px; border-top: 1px solid var(--rule-soft); }
  /* background-color, not the `background` shorthand — the shorthand resets
     background-image and would strip the hazard tape off this button on
     exactly the screens where it is the only call to action. */
  .nav-cta a { background-color: var(--blue); color: var(--white); font-weight: 600; text-align: center; }
  .nav-cta a:hover { background-color: var(--blue-dark); }

  .services__grid { grid-template-columns: 1fr; max-width: 560px; }
  .projects__headline { grid-template-columns: 1fr; align-items: start; }
  .project { flex-basis: 88%; }
}

@media (max-width: 620px) {
  :root { --notch: 18px; }
  .hero__frame { min-height: 100svh; }
  .hero__scroll { display: none; }

  /* Panel sits fully inside the frame here. Bleeding it off the bottom edge
     works on desktop, but at this width the frame clips it into a hard cut
     that reads as a rendering fault. */
  .hero__body { padding-bottom: clamp(18px, 4vw, 26px); }
  /* The panel stacks here, so it's roughly twice as tall and hangs further
     into the section below. Both clearances scale with it. */
  .hero__body { padding-bottom: 116px; }
  .services { padding-top: calc(var(--section) + 78px); }
  .hero__panel {
    width: 100%;
    flex-direction: column; align-items: stretch;
    padding: 20px;
  }
  .hero__panel p { max-width: none; }
  .hero__panel .btn { justify-content: center; }

  .logo__mark { width: 40px; flex-basis: 40px; }
  .logo__name { font-size: 1.2rem; }

  /* Cards are narrow here, so the notch fillets read as stray marks and the
     inset bar leaves too little room for a long name. Full-width bar instead. */
  .member { flex-basis: min(74vw, 280px); }
  /* 1.13 scale pushed the active card wider than the viewport, and
     .team__viewport clips the overflow — the left end of the name and role
     were cut off. Scale gently and let the shadow carry the emphasis. */
  .member.is-active { transform: scale(1.03); }
  .member__info { left: 0; padding: 12px 14px 13px; border-top-left-radius: 0; }
  .member__info .fillet { display: none; }
  .member__name { font-size: .7rem; }

  /* Three stacked link groups made the footer a long scroll; pair them up. */
  .footer__grid { grid-template-columns: 1fr 1fr; gap: 26px 20px; }
  .footer__about { grid-column: 1 / -1; }

  /* Footer links rendered 14px tall — too small to tap reliably. Padding
     brings the hit area to 40px without changing how they look. */
  .footer__col li + li { margin-top: 2px; }
  .footer__col a { display: inline-block; padding-block: 12px; }
  .socials { gap: 10px; }
  .socials a { padding: 10px; }
  .logo { padding-block: 6px; }

  .footer__bottom { justify-content: center; text-align: center; }
  .lead__fields { flex-direction: column; align-items: stretch; }
  .lead input { flex: 1 1 auto; width: 100%; text-align: center; }
  .lead button { width: 100%; flex: 0 0 auto; border-radius: var(--r-pill); }
  .lede { text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
  /* Slideshow holds on the first frame rather than cycling. */
  .hero__slide img, .hero__slide .ph { animation: none !important; transform: none !important; }
  .hero__scroll span { animation: none !important; }
}

@media print {
  .site-header, .cta, .nav-btn, .menu-toggle { display: none; }
  .hero__frame::after, .services::before { display: none; }
}

/* ===========================================================================
   ABOUT PAGE
   =========================================================================== */

/* ── page hero ───────────────────────────────────────── */
.page-hero { position: relative; }
.page-hero__frame {
  position: relative;
  overflow: hidden;
  /* Full-bleed to match the homepage hero, so the header plates meet the
     photo at the screen edge on both pages. Taller than the copy needs, so
     the team photograph has room between the type and the strip below. */
  min-height: clamp(460px, 70vh, 680px);
  display: grid;
  place-items: center;
  text-align: center;
}
.page-hero__media { position: absolute; inset: 0; }
/* The comp desaturates this photo so the white type stays legible. */
.page-hero__media img, .page-hero__media .ph { width: 100%; height: 100%; object-fit: cover; filter: grayscale(.7) contrast(1.05); }
.page-hero__media picture { display: block; width: 100%; height: 100%; }
.page-hero__frame::after {
  content: ""; position: absolute; inset: 0;
  /* Centred text, so an even wash rather than the homepage's left wedge.
     Tuned against the services photograph: the headline lands at 8.9:1 and
     the paragraph at 6.1:1, both clear of their thresholds, while showing
     more of the image than the heavier scrim it replaces. */
  background: linear-gradient(180deg, rgba(14,16,19,.46), rgba(14,16,19,.62));
}
/* Top padding clears the header plates, which stand 82px tall; the previous
   60px left the title tucked into them. Bottom is sized so the frame fills
   without a band of dead space under the CTA. */
.page-hero__body { position: relative; z-index: 2; width: 100%; padding-block: clamp(92px, 10vw, 132px) clamp(58px, 7vw, 96px); }
.page-hero__title {
  color: var(--white);
  margin-bottom: 18px;
  text-shadow: 0 2px 18px rgba(14, 16, 19, .34);
}
.page-hero__body p { color: rgba(255,255,255,.94); font-size: clamp(.85rem, 1.15vw, .97rem); line-height: 1.8; max-width: 68ch; margin-inline: auto; }

/* Six portrait cards straddling the bottom edge of the hero. */
.hero-strip {
  position: relative;
  z-index: 3;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: clamp(8px, 1.2vw, 18px);
  /* Rides high enough to straddle the frame edge, but not so high that it
     crowds the paragraph above — the tighter body padding reduced that gap. */
  margin-top: clamp(-96px, -6.6vw, -64px);
  padding-inline: clamp(6px, 2vw, 40px);
  margin-bottom: clamp(46px, 6vw, 78px);
}
/* Portrait ratio rather than the near-square the placeholders used — these
   are headshots, and 1:1.06 cropped them at the shoulders. */
.hero-strip figure { border-radius: var(--r-lg); overflow: hidden; aspect-ratio: 4 / 5; box-shadow: var(--shadow-md); }
.hero-strip figure:nth-child(odd) { margin-top: clamp(8px, 1.4vw, 18px); }
.hero-strip picture { display: block; width: 100%; height: 100%; }
.hero-strip img, .hero-strip .ph { width: 100%; height: 100%; object-fit: cover; object-position: center 22%; }

/* ── team rail ───────────────────────────────────────── */
.team-rail { --rail-gap: clamp(16px, 2.2vw, 30px); margin-top: clamp(44px, 5.5vw, 70px); }

.team-rail__head { display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-bottom: clamp(18px, 2.4vw, 26px); }
.team-rail__title { font-size: clamp(1.15rem, 1.9vw, 1.5rem); font-weight: 700; letter-spacing: -.015em; }
.team-rail__bar { display: flex; align-items: center; gap: 12px; }

.team-rail__track {
  display: flex;
  gap: var(--rail-gap);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* Room for the hover lift, which overflow-x would otherwise clip. */
  padding: 8px 0 14px;
  margin-block: -8px -14px;
}
.team-rail__track::-webkit-scrollbar { display: none; }

/* Two per view, with a sliver of the third showing so the row reads as
   scrollable. The rail advances two at a time — see data-rail-step. */
.mate {
  flex: 0 0 calc((100% - var(--rail-gap)) / 2.12);
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  aspect-ratio: 5 / 6;
  scroll-snap-align: start;
  /* Shows through the portrait's edges and behind the notch, same treatment
     as the homepage member cards. */
  background: linear-gradient(180deg, #86B6E8 0%, #5C93D6 100%);
  box-shadow: var(--shadow-md);
  transition: transform .32s ease, box-shadow .32s ease;
}
/* Alternating blue and orange, as on the homepage team carousel. */
.mate:nth-child(even) { background: linear-gradient(180deg, var(--orange) 0%, var(--orange-deep) 100%); }
.mate:hover { transform: translateY(-6px); box-shadow: var(--shadow-lg); }

.mate__photo { position: absolute; inset: 0; }
.mate__photo picture { display: block; width: 100%; height: 100%; }
.mate__photo img { width: 100%; height: 100%; object-fit: cover; object-position: center 18%; }

/* Info plate tucked into the bottom-right; the card curves in to meet it.
   This is the notch motif that recurs across the site. */
.mate__info {
  position: absolute;
  left: 14%; right: 0; bottom: 0;
  z-index: 2;
  background: var(--white);
  padding: clamp(13px, 1.5vw, 18px) clamp(15px, 1.7vw, 22px) clamp(14px, 1.6vw, 19px);
  border-top-left-radius: var(--r-md);
  /* Several of these portraits are shot on white, and a white plate on a
     white background has no edge at all — the text just floated. The shadow
     defines the top and left edges without adding a hard rule. */
  box-shadow: -3px -3px 16px rgba(14, 16, 19, .14);
}
.mate__name {
  font-size: clamp(.76rem, 1.05vw, .92rem);
  font-weight: 800;
  letter-spacing: .015em;
  text-transform: uppercase;
  line-height: 1.3;
}
.mate__role {
  margin-top: 6px;
  font-size: clamp(.62rem, .82vw, .72rem);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--blue);
}
.mate:nth-child(even) .mate__role { color: var(--orange-deep); }
.mate__role:empty { display: none; }

@media (max-width: 720px) {
  /* Two of these on a phone would be thumbnails. One and a bit instead. */
  .mate { flex-basis: 78%; aspect-ratio: 5 / 6.4; }
  .mate__info { left: 10%; }
}

@media (prefers-reduced-motion: reduce) {
  .team-rail__track { scroll-behavior: auto; }
  .mate:hover { transform: none; }
}

/* ── intro: blue-then-black headline beside body copy ── */
.about-intro { display: grid; grid-template-columns: 1.08fr 1fr; gap: clamp(28px, 6vw, 86px); align-items: start; }
.about-intro .h2 { max-width: 13ch; }
.about-intro .h2 .accent { color: var(--blue); }
.about-intro__body p { font-size: .8rem; line-height: 1.72; color: var(--ink-2); text-align: justify; }
.about-intro__body p + p { margin-top: 16px; }
.about-intro__body strong { font-weight: 700; }

/* ── value cards ─────────────────────────────────────── */
.values { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(16px, 2.6vw, 34px); margin-top: clamp(44px, 6vw, 76px); }
.value {
  display: flex; align-items: flex-start; gap: 16px;
  background: var(--white);
  border-radius: var(--r-md);
  padding: 18px 22px 18px 18px;
  box-shadow: 0 6px 22px rgba(14,16,19,.10);
}
/* Slight rise to the right, as in the comp. */
.values .value:nth-child(2) { transform: translateY(-6px); }
.values .value:nth-child(3) { transform: translateY(-12px); }
.value__icon {
  flex: 0 0 62px; width: 62px; height: 62px;
  display: grid; place-items: center;
  border-radius: var(--r-md);
  background: var(--blue); color: var(--white);
}
.value__icon svg { width: 32px; height: 32px; }
.value h3 { font-size: .96rem; font-weight: 700; margin-bottom: 6px; }
.value p { font-size: .72rem; line-height: 1.62; color: var(--gray); }

/* ── people ──────────────────────────────────────────── */
.people__title { text-align: center; margin-bottom: clamp(38px, 5vw, 64px); }

.person { position: relative; padding-top: 34px; }
.person + .person { margin-top: clamp(40px, 5.4vw, 72px); }
.person__panel {
  background: var(--surface);
  border-radius: var(--r-lg);
  padding: clamp(20px, 2.6vw, 30px);
  display: grid;
  grid-template-columns: clamp(230px, 27%, 320px) 1fr;
  gap: clamp(22px, 3.4vw, 48px);
  align-items: start;
}
.person__photo {
  position: relative;
  border-radius: var(--r-md);
  overflow: hidden;
  aspect-ratio: 5 / 7;
  background: linear-gradient(180deg, #86B6E8 0%, #5C93D6 100%);
  margin-top: 22px;
}
.person:nth-child(even) .person__photo { background: linear-gradient(180deg, var(--orange) 0%, var(--orange-deep) 100%); }
.person__photo picture { display: block; width: 100%; height: 100%; }
.person__photo img, .person__photo .ph { width: 100%; height: 100%; object-fit: cover; object-position: top center; }

.person__cv {
  position: absolute;
  left: 12px; right: 12px; bottom: 12px;
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  padding: 11px 14px;
  border-radius: var(--r-sm);
  background: var(--blue); color: var(--white);
  font-size: .78rem; font-weight: 500;
  transition: background .18s ease, transform .18s ease;
}
.person__cv:hover { background: var(--blue-dark); transform: translateY(-2px); }
.person__cv svg { width: 15px; height: 15px; flex: 0 0 15px; }

/* Name banner rises above the panel's top edge. */
.person__banner {
  position: absolute;
  top: 0;
  left: calc(clamp(230px, 27%, 320px) + clamp(42px, 6vw, 78px));
  right: clamp(30px, 8vw, 110px);
  background: var(--blue);
  color: var(--white);
  border-radius: var(--r-md);
  padding: 15px 26px;
  z-index: 2;
}
.person__name { font-size: clamp(1.05rem, 1.7vw, 1.4rem); font-weight: 800; letter-spacing: .01em; text-transform: uppercase; line-height: 1.2; }
.person__role { font-size: .78rem; letter-spacing: .06em; text-transform: uppercase; opacity: .95; }

.person__bio { padding-top: clamp(52px, 6vw, 74px); }
.person__bio p { font-size: .84rem; line-height: 1.85; color: var(--ink-2); text-align: justify; }
.person__bio p + p { margin-top: 14px; }
.person__bio strong { font-weight: 700; color: var(--ink); }

.person__creds { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
.person__creds span {
  font-size: .68rem; font-weight: 500;
  padding: 7px 14px;
  border-radius: var(--r-pill);
  background: var(--white);
  border: 1px solid var(--rule);
  color: var(--ink-2);
}

/* ── about responsive ────────────────────────────────── */
@media (max-width: 1080px) {
  .about-intro { grid-template-columns: 1fr; }
  .about-intro .h2 { max-width: 18ch; }
  .values { grid-template-columns: 1fr; }
  .values .value:nth-child(2), .values .value:nth-child(3) { transform: none; }
  /* Three across, not two — six cards in two columns makes three deep rows
     that push the intro copy well down the page. */
  .hero-strip { grid-template-columns: repeat(3, 1fr); margin-top: clamp(-90px, -9vw, -70px); }
}

@media (max-width: 760px) {
  .person__panel { grid-template-columns: 1fr; }
  .person__photo { max-width: 300px; margin-inline: auto; }
  .person__banner { position: static; margin-bottom: 18px; }
  .person__bio { padding-top: 0; }
  .person { padding-top: 0; }
  .person__bio p, .about-intro__body p { text-align: left; }
}

@media (max-width: 620px) {
  .hero-strip { grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: -46px; padding-inline: 4px; }
  .hero-strip figure:nth-child(odd) { margin-top: 0; }
}

/* ===========================================================================
   SERVICES PAGE
   =========================================================================== */

/* Hero variant carrying a CTA and the category strip below it. */
.page-hero__cta { margin-top: clamp(22px, 3vw, 32px); }
.btn--quote { background: var(--white); color: var(--blue); font-weight: 600; border-radius: var(--r-pill); padding: 15px 34px; }
.btn--quote:hover { background: var(--blue); color: var(--white); }

/* Four category chips straddling the bottom edge of the hero. */
.cat-strip {
  position: relative;
  z-index: 3;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(12px, 1.8vw, 24px);
  margin-top: clamp(-44px, -3.2vw, -28px);
  margin-bottom: clamp(46px, 6vw, 80px);
}
.cat {
  display: flex; align-items: center; gap: 14px;
  background: var(--white);
  border-radius: var(--r-md);
  padding: 14px 18px 14px 14px;
  box-shadow: var(--shadow-md);
}
.cat__icon {
  flex: 0 0 52px; width: 52px; height: 52px;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: var(--blue); color: var(--white);
}
.cat__icon svg { width: 27px; height: 27px; }
.cat p { font-size: clamp(.85rem, 1.1vw, .97rem); font-weight: 600; line-height: 1.28; }

/* ── service cards ───────────────────────────────────── */
.svc-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(20px, 2.6vw, 34px);
  align-items: start;
}
.svc-item {
  background: var(--surface);
  border-radius: var(--r-lg);
  overflow: hidden;
  transition: transform .25s ease, box-shadow .25s ease;
}
.svc-item:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }

.svc-item__media { position: relative; aspect-ratio: 16 / 9; }
.svc-item__media img, .svc-item__media .ph { width: 100%; height: 100%; object-fit: cover; }

/* Number badge straddles the lower-left corner of the image. */
.svc-item__no {
  position: absolute;
  left: clamp(16px, 2vw, 26px);
  bottom: calc(clamp(28px, 3.2vw, 40px) * -0.5);
  min-width: clamp(52px, 5vw, 62px);
  height: clamp(52px, 5vw, 62px);
  display: grid; place-items: center;
  padding-inline: 8px;
  border-radius: var(--r-sm);
  background: var(--white);
  color: var(--ink);
  font-size: clamp(1.15rem, 1.7vw, 1.45rem);
  font-weight: 700;
  letter-spacing: .01em;
  box-shadow: var(--shadow-sm);
}
.svc-item__body { padding: clamp(30px, 3.6vw, 44px) clamp(20px, 2.4vw, 30px) clamp(24px, 3vw, 34px); }
.svc-item__body h3 {
  font-size: clamp(1.12rem, 1.75vw, 1.42rem);
  font-weight: 700;
  line-height: 1.22;
  letter-spacing: -.012em;
  margin-bottom: 12px;
  max-width: 22ch;
}
.svc-item__body p { font-size: .78rem; line-height: 1.72; color: var(--ink-2); text-align: justify; }

/* One service carries markedly longer copy, so it runs full width with the
   image beside the text rather than forcing a very tall single column. */
.svc-item--wide { grid-column: 1 / -1; display: grid; grid-template-columns: 1fr 1.15fr; }
.svc-item--wide .svc-item__media { aspect-ratio: auto; height: 100%; min-height: 280px; }
.svc-item--wide .svc-item__no { left: clamp(16px, 2vw, 26px); bottom: clamp(16px, 2vw, 26px); }
.svc-item--wide .svc-item__body { padding: clamp(26px, 3vw, 40px); align-self: center; }

@media (max-width: 900px) {
  .cat-strip { grid-template-columns: repeat(2, 1fr); margin-top: clamp(-70px, -7vw, -50px); }
  .svc-list { grid-template-columns: 1fr; }
  .svc-item--wide { grid-column: auto; grid-template-columns: 1fr; }
  .svc-item--wide .svc-item__media { aspect-ratio: 16 / 9; min-height: 0; }
  .svc-item--wide .svc-item__no { bottom: calc(clamp(28px, 3.2vw, 40px) * -0.5); }
}

@media (max-width: 560px) {
  .cat-strip { grid-template-columns: 1fr; gap: 10px; margin-top: -46px; }
  .svc-item__body p { text-align: left; }
}

/* ===========================================================================
   PROJECTS PAGE
   =========================================================================== */
.proj-rail { --rail-gap: clamp(18px, 2.2vw, 28px); }

.proj-rail__bar { display: flex; align-items: center; gap: 14px; margin-bottom: clamp(20px, 2.6vw, 30px); }

/* One row that scrolls sideways, snapping card to card. overflow-x forces
   overflow-y to a non-visible value, which would clip the card's hover lift
   and shadow — the inline padding buys the room back. */
.proj-rail__track {
  display: flex;
  gap: var(--rail-gap);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 10px 0 16px;
  margin: -10px 0 -16px;
}
.proj-rail__track::-webkit-scrollbar { display: none; }

/* Three across on a wide screen, sized so the next card peeks past the edge.
   Fitting exactly three left a clean right margin and no hint that a fourth
   project existed. */
.proj-rail__track > .proj-card {
  flex: 0 0 calc((100% - 2 * var(--rail-gap)) / 3.3);
  width: auto;
  align-self: stretch;
  scroll-snap-align: start;
}

@media (max-width: 980px) {
  .proj-rail__track > .proj-card { flex-basis: calc((100% - var(--rail-gap)) / 2.2); }
}

@media (prefers-reduced-motion: reduce) {
  .proj-rail__track { scroll-behavior: auto; }
}

/* The whole card is the trigger, so it's a button rather than a div with a
   click handler — keyboard and screen readers get it for free. */
.proj-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  text-align: left;
  background: var(--surface);
  border: 0;
  border-radius: var(--r-lg);
  overflow: hidden;
  cursor: pointer;
  transition: transform .26s ease, box-shadow .26s ease;
}
.proj-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-lg); }
.proj-card:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; }

.proj-card__media { position: relative; aspect-ratio: 4 / 3; overflow: hidden; flex: 0 0 auto; }
.proj-card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; }
.proj-card:hover .proj-card__media img { transform: scale(1.05); }
.proj-card__media::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(14,16,19,0) 55%, rgba(14,16,19,.55) 100%);
}

.proj-card__status {
  position: absolute; top: 16px; left: 16px; z-index: 2;
  padding: 7px 15px;
  border-radius: var(--r-pill);
  font-size: .68rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  background: var(--white); color: var(--ink);
}
.proj-card__status--live { background: var(--orange); color: var(--white); }
.proj-card__status--design { background: var(--blue); color: var(--white); }

/* Photo count doubles as the affordance that the card opens something. */
.proj-card__count {
  position: absolute; right: 16px; bottom: 16px; z-index: 2;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 16px;
  border-radius: var(--r-pill);
  background: rgba(255,255,255,.94); color: var(--ink);
  font-size: .74rem; font-weight: 600;
}
.proj-card__count svg { width: 14px; height: 14px; }

.proj-card__body { padding: clamp(20px, 2.4vw, 28px); flex: 1 1 auto; }
.proj-card__title { font-size: clamp(1.05rem, 1.6vw, 1.32rem); font-weight: 700; line-height: 1.24; letter-spacing: -.012em; }
.proj-card__loc:empty { display: none; }
.proj-card__loc { display: flex; align-items: flex-start; gap: 7px; margin-top: 10px; font-size: .78rem; color: var(--gray); line-height: 1.5; }
.proj-card__loc svg { width: 13px; height: 13px; flex: 0 0 13px; margin-top: 3px; }
.proj-card__specs { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 16px; }
.proj-card__specs span {
  font-size: .68rem; font-weight: 500;
  padding: 6px 13px;
  border-radius: var(--r-pill);
  background: var(--white);
  border: 1px solid var(--rule);
  color: var(--ink-2);
}

/* ── lightbox ────────────────────────────────────────── */
.lb {
  position: fixed; inset: 0; z-index: 100;
  display: none;
  background: rgba(8, 9, 11, .94);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
/* The column cap matters as much as the row cap. Left as the default `auto`,
   the column sizes to max-content — and the scroll track's max-content is
   every slide laid end to end, so the modal grew to eleven screens wide. */
.lb.is-open { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; grid-template-columns: minmax(0, 1fr); }

.lb__bar { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: clamp(14px, 2.4vw, 24px) clamp(16px, 4vw, 40px); color: var(--white); }
.lb__heading { min-width: 0; }
.lb__title { font-size: clamp(.95rem, 1.5vw, 1.15rem); font-weight: 700; line-height: 1.3; }
.lb__loc { font-size: .74rem; color: rgba(255,255,255,.66); margin-top: 3px; }
.lb__close {
  flex: 0 0 44px; width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1.5px solid rgba(255,255,255,.34);
  color: var(--white);
  transition: background .18s ease, border-color .18s ease;
}
.lb__close:hover { background: rgba(255,255,255,.14); border-color: var(--white); }
.lb__close svg { width: 18px; height: 18px; }

/* min-height:0 on the item plus minmax(0,1fr) on the track — both are needed,
   or the image sizes the row rather than the row bounding the image. */
.lb__stage {
  position: relative;
  display: grid;
  /* Both tracks capped. Without minmax(0,...) the stage's own row grew to the
     image's natural height, so height:100% on the image resolved against
     1124px rather than the 735px stage it sits in. */
  grid-template-rows: minmax(0, 1fr);
  grid-template-columns: minmax(0, 1fr);
  place-items: center;
  padding: clamp(8px, 1.5vh, 20px) clamp(56px, 7vw, 96px);
  min-height: 0;
  overflow: hidden;
}
/* The track is a real horizontal scroller, not a transformed strip. Snap
   points do the positioning, so touch swipe and trackpad scroll are native
   and the arrows just call scrollTo. */
.lb__track {
  grid-area: 1 / 1;
  justify-self: stretch;
  align-self: stretch;
  min-width: 0;
  min-height: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.lb__track::-webkit-scrollbar { display: none; }
.lb__track:focus-visible { outline: 2px solid rgba(255,255,255,.55); outline-offset: 3px; }

.lb__slide {
  position: relative;
  flex: 0 0 100%;
  min-width: 0;
  align-self: stretch;
  scroll-snap-align: center;
  scroll-snap-stop: always;
}
/* Absolute against the slide, so the photo's height resolves against a box
   the photo isn't helping to size. Percentage heights inside a flex item
   whose own height comes from stretch resolve circularly, and the image won
   — it overflowed the stage by 66px. */
.lb__slide picture { position: absolute; inset: 0; display: block; }

/* Fill the slide and let contain do the scaling. max-height:100% on a grid
   item did not resolve against the track here — the image kept its natural
   1124px against a 735px stage — and this cannot fail the same way. */
.lb__slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
}
.lb__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 48px; height: 48px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(255,255,255,.14);
  color: var(--white);
  transition: background .18s ease;
}
.lb__nav:hover { background: rgba(255,255,255,.28); }
.lb__nav:disabled { opacity: .25; cursor: not-allowed; }
.lb__nav svg { width: 20px; height: 20px; }
.lb__nav--prev { left: clamp(6px, 1.2vw, 16px); }
.lb__nav--next { right: clamp(6px, 1.2vw, 16px); }

.lb__foot { display: flex; align-items: center; gap: clamp(12px, 2vw, 22px); padding: clamp(12px, 2vw, 20px) clamp(16px, 4vw, 40px); }
.lb__count { flex: 0 0 auto; color: rgba(255,255,255,.7); font-size: .78rem; font-variant-numeric: tabular-nums; }

.lb__thumbs {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  gap: 8px;
  padding: 3px 0;
  overflow-x: auto;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  /* `safe` keeps the first thumb reachable once the strip overflows; browsers
     without it fall back to flex-start, which is the same behaviour. */
  justify-content: safe center;
}
.lb__thumbs::-webkit-scrollbar { display: none; }

.lb__thumb {
  flex: 0 0 auto;
  width: 68px; height: 46px;
  border-radius: 7px;
  overflow: hidden;
  opacity: .4;
  outline: 2px solid transparent;
  outline-offset: 1px;
  transition: opacity .18s ease, outline-color .18s ease;
}
.lb__thumb picture { display: block; width: 100%; height: 100%; }
.lb__thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.lb__thumb:hover { opacity: .78; }
.lb__thumb.is-active { opacity: 1; outline-color: var(--white); }

body.lb-open { overflow: hidden; }

@media (max-width: 620px) {
  .proj-rail__track > .proj-card { flex-basis: 82%; }
  /* Swipe is the primary gesture here, so stop reserving 96px of gutter for
     the arrows — they overlay the photo edges instead and the image gets the
     full width of the screen. */
  .lb__nav { width: 40px; height: 40px; background: rgba(255,255,255,.18); backdrop-filter: blur(4px); }
  .lb__stage { padding-inline: 6px; }
  .lb__nav--prev { left: 6px; }
  .lb__nav--next { right: 6px; }
  .lb__thumb { width: 54px; height: 38px; }
}

@media (prefers-reduced-motion: reduce) {
  .proj-card:hover { transform: none; }
  .proj-card:hover .proj-card__media img { transform: none; }
  .lb__track,
  .lb__thumbs { scroll-behavior: auto; }
}
