/* ============================================================
   Winning Development - site styles
   Extracted from the single-file build so every page can share
   one copy. Edit this file, not a copy inside any .html file.
   ============================================================ */

:root{
    /* ================= HARBOUR BLUE + SALT =================
       #1E3A5C slate navy  · #152B44 deep harbour · #B9C6D4 harbour grey
       #E6EBF0 salt        · #F5F8FA linen        · #9AAFC4 pewter blue
       #7A2E3C oxblood     (burgundy accent, used sparingly)          */
    --color-navy:#1E3A5C;        /* primary dark - nav, dark sections */
    --color-midnight:#152B44;    /* deepest - footer, statement blocks */
    --color-harbour:#B9C6D4;     /* secondary text on dark, captions */
    --color-salt:#E6EBF0;        /* alternating light sections */
    --color-linen:#F5F8FA;       /* primary light background */
    --color-pewter:#9AAFC4;      /* muted blue detail */
    --color-brass:#7A2E3C;       /* burgundy - fine rules, hovers, small marks only */
    --color-text-secondary:#5E7186;
    --color-border:rgba(30,58,92,0.12);

    /* ---- GLASS: translucent white layers. blur alone looks like frosted
            plastic; the saturate() is what makes colour bloom through the
            panel and read as glass rather than fog ---- */
    --glass-1:rgba(255,255,255,.48);   /* lightest - over busy video      */
    --glass-2:rgba(255,255,255,.66);   /* standard panel                  */
    --glass-3:rgba(255,255,255,.82);   /* dense - modals, menu            */
    --glass-blur:saturate(180%) blur(20px);
    --glass-blur-lg:saturate(190%) blur(44px);
    --glass-edge:rgba(255,255,255,.70);
    /* specular top edge + inner rim: the two highlights that sell the
       illusion of a thick pane catching light                            */
    --glass-hi:inset 0 1px 0 rgba(255,255,255,.92);
    --glass-rim:inset 0 0 0 1px rgba(255,255,255,.40);
    --spring:cubic-bezier(.34,1.4,.64,1);   /* gel overshoot */

    /* ================= TYPE =================
       Three families, each with one job:
         DISPLAY  Instrument Sans - a modern grotesque. Chosen over a geometric
                  face because its apertures are tighter and its letterforms
                  carry actual shape, which is what stops large headlines from
                  reading as blocky. Set at 600 with heavy NEGATIVE tracking:
                  a grotesque at display size looks loose until you close it up.
         UI       Jost - geometric sans, kept for the wordmark and every
                  tracked-out uppercase label, so the caps all share one voice.
         TEXT     Inter - built for small sizes; runs all body copy.
       --f-display is its own token, so swapping the headline face is a
       one-line change.
       Tracking runs opposite to size: large display goes NEGATIVE (big type
       looks loose by default), small caps go very POSITIVE.                  */
    --f-display:'Instrument Sans', 'Helvetica Neue', Arial, sans-serif;
    --f-ui:'Jost', 'Futura', sans-serif;
    --f-text:'Inter', system-ui, sans-serif;

    --ls-display:-0.032em;   /* huge headlines  */
    --ls-head:-0.022em;      /* mid headlines   */
    --ls-label:0.34em;       /* micro caps      */
    --ls-nav:0.14em;         /* nav / big caps  */
    --lh-display:1.04;
    --lh-head:1.1;
    --lh-text:1.72;

    /* ---- radii: one soft family, scaled by element size ---- */
    --r-xs:6px;    /* dots, chips, tiny controls   */
    --r-sm:10px;   /* inputs, small buttons        */
    --r-md:16px;   /* cards, buttons               */
    --r-lg:22px;   /* media frames, bio panels     */
    --r-xl:28px;   /* modal panel, hero-scale media*/

    /* ---- elevation: two layers each (contact shadow + ambient spread),
            tinted with the navy ink so shadows read blue-grey, never muddy ---- */
    --sh-sm:0 1px 2px rgba(21,43,68,.04), 0 3px 8px rgba(21,43,68,.04);
    --sh-md:0 2px 6px rgba(21,43,68,.05), 0 12px 28px rgba(21,43,68,.07);
    --sh-lg:0 4px 12px rgba(21,43,68,.06), 0 26px 60px rgba(21,43,68,.10);
    --sh-xl:0 10px 28px rgba(21,43,68,.08), 0 54px 120px rgba(21,43,68,.14);
    --ease:cubic-bezier(.16,.9,.3,1);

    /* dark ladder */
    --ox-deep:#152B44;
    --ox-mid:#1E3A5C;
    --ox-soft:#1E3A5C;
    --navy-deep:#152B44;
    --navy-mid:#1E3A5C;
    --navy-soft:rgba(245,248,250,0.14);   /* fine rules on dark */

    /* light ladder - the site's primary surfaces */
    --white:#FFFFFF;       /* pure - glass sits on this */
    --white-2:#E6EBF0;     /* Salt - alternating sections */
    --white-3:#B9C6D4;     /* Harbour grey - rules, chips */
    --bg:#F5F8FA;          /* page field - Linen, per the brand system */

    --muted-d:#B9C6D4;     /* secondary text on dark */
    --muted-l:#5E7186;     /* secondary text on light */

    --ink-rgb:21,43,68;
    --white-rgb:245,248,250;

    /* legacy tokens mapped onto the palette */
    --color-charcoal:#1E3A5C;
    --color-black:#152B44;
    --color-cashmere:#B9C6D4;
    --color-bone:#E6EBF0;
    --color-ivory:#FFFFFF;
    --color-bronze:#7A2E3C;
    --ivory:#FFFFFF;
    --dark:#1E3A5C;
    --dark-2:#1E3A5C;
    --taupe:#5E7186;
    --limestone:#E6EBF0;
    --brass:#7A2E3C;
    --emerald:#1E3A5C;
    --espresso:#152B44;
    --cognac:#B9C6D4;
    --amber:#7A2E3C;
    --champagne:#B9C6D4;
    --blush:#FFFFFF;
    --ink:#1E3A5C;
    --walnut:#1E3A5C;
    --stone:#1E3A5C;
    --stone-2:rgba(245,248,250,0.14);
    --grey-800:#152B44;
    --grey-600:#1E3A5C;

    /* accent token on LIGHT surfaces: navy */
    --green:#1E3A5C;
    --green-hi:#152B44;
    --green-dim:#5E7186;
  }

  /* light theme throughout - the only inverted context is type sitting
     directly on a photograph (.svc-cap), handled locally */


  *{margin:0;padding:0;box-sizing:border-box;}
  html{scroll-behavior:smooth;}
  /* The weighted-scroll loop in site.js drives window.scrollTo itself every
     frame; native smooth behaviour would try to animate each of those and
     fight it, so it is switched off while that loop is active. The class is
     only added when the effect actually runs, so no-JS and reduced-motion
     visitors keep the native smooth anchors. */
  html.has-weighted-scroll{scroll-behavior:auto;}
  body{font-family:var(--f-text); font-weight:300; color:var(--dark-2);
       font-size:16.5px; line-height:var(--lh-text); letter-spacing:.004em;
       -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
       text-rendering:optimizeLegibility; font-optical-sizing:auto;
       overflow-x:hidden;
       background:var(--bg);
       background-image:
         radial-gradient(60% 42% at 12% 4%,  rgba(158,186,214,.26) 0%, transparent 62%),
         radial-gradient(52% 38% at 92% 26%, rgba(185,198,212,.22) 0%, transparent 60%),
         radial-gradient(64% 46% at 46% 96%, rgba(122,46,60,.07) 0%, transparent 66%);
       background-attachment:fixed;}
  /* editorial high-contrast serif for major headlines */
  h1,h2,h3,h4{font-family:var(--f-display); font-weight:600; text-transform:none;
              line-height:var(--lh-head); letter-spacing:var(--ls-head);
              color:var(--color-charcoal); font-optical-sizing:auto;}
  /* the biggest headlines get the tightest tracking and leading */
  h1,h2{letter-spacing:var(--ls-display); line-height:var(--lh-display);}
  /* emphasis by weight AND slope - a light italic against medium roman reads
     far more modern than a colour change */
  h1 em,h2 em,h3 em{font-style:italic; font-weight:400;}
  .dark h1,.dark h2,.dark h3,.dark h4,
  .city-band h1,.city-band h2,.city-band h3,.city-band h4,
  .hero h1,footer h1,footer h2,footer h3,.menu h1,.menu h2{color:var(--color-charcoal);}
  /* nav, labels, buttons, captions, project data stay sans */
  .tag,.label,.send,.brand,.logo,
  .svc-cap span,.hs-card .info span,.specs div b,.pr-bio .facts b,
  .stat span,.loc,.role,.n,.idx-row .n{font-family:var(--f-ui);}
  /* small titles drop the display face at sizes where it loses definition */
  .hs-card .info h3,.svc-cap h4,.pj-impact h4{font-family:var(--f-ui);}

  /* ---- where the oxblood lives: micro eyebrow labels only. Each mark is a
     few characters, but they recur in every section, so the accent reads as
     deliberate without ever taking up area. Captions sitting ON photographs
     are excluded - they stay light, since a dark accent would vanish. ---- */
  .hs-card .info span, .specs div b, .pr-bio .facts b, .pr-bio .role,
  .c-info .row b, .feature-info .loc, .pj-facts div b,
  .pj-impact h4{color:var(--brass);}
  a{text-decoration:none; color:inherit;}
  img{display:block; width:100%; height:100%; object-fit:cover;}
  ul{list-style:none;}
  .wrap{max-width:1600px; margin:0 auto; padding:0 clamp(20px,4vw,64px);}
  .tag{display:inline-block; text-transform:uppercase; letter-spacing:var(--ls-label);
       font-size:10px; font-weight:500; color:var(--brass); margin-right:calc(var(--ls-label) * -1);}
  em{font-style:normal; color:var(--green);}
  /* No ::selection override - highlighting falls back to the browser/OS
     default blue, which is what people expect everywhere else. */
  .dark{background:transparent; color:var(--color-charcoal);}
  .dark h1,.dark h2,.dark h3,.dark h4{color:var(--color-charcoal);}
  .muted{color:var(--taupe);}
  .dark .muted{color:var(--muted-l);}

  @supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
    :root{--glass-1:rgba(255,255,255,.86); --glass-2:rgba(255,255,255,.92); --glass-3:rgba(255,255,255,.97);}
  }
  /* respect reduced-motion / reduced-transparency preferences */
  @media (prefers-reduced-motion:reduce){
    *{animation-duration:.001ms !important; transition-duration:.001ms !important;}
    /* don't merely shorten the reveal - skip it. Otherwise content still starts
       at opacity 0 and depends on an observer firing to become readable. */
    .js .rv{opacity:1 !important; transform:none !important; filter:none !important;}
  }

  /* ================= LIQUID GLASS =================
     Apple describes the material as three stacked layers, and that is exactly
     how this is built:
       ILLUMINATION  the element itself - blur + saturate + brightness on the
                     backdrop. saturate is what makes colour bloom through so
                     it reads as glass rather than fog.
       HIGHLIGHT     ::before - a masked ring carrying a conic gradient, so the
                     bevel is bright on one side and dim on the other, the way
                     a real lens catches a light source. On small controls the
                     ring ALSO re-filters the backdrop at higher brightness and
                     saturation, which is what produces the "light concentrating
                     at the edge" read - the cheap stand-in for true lensing.
       SHADOW        outer ambient shadow + an inset bottom shade that gives the
                     pane apparent thickness.
     Apple's own guidance: bigger surface = thicker material = more blur and a
     deeper shadow. --lq-blur is set per element to honour that.
     (.stat is intentionally NOT part of this system - the hero stats are a
     plain white-on-navy treatment, no card.) */
  .lq,
  .burger, .send, .pj-close, .pj-nav i, .pj-plus,
  .pj-panel, .pr-bio{
    position:relative; isolation:isolate;
    background:linear-gradient(150deg, rgba(255,255,255,.58) 0%,
                                       rgba(255,255,255,.30) 46%,
                                       rgba(255,255,255,.52) 100%);
    -webkit-backdrop-filter:blur(var(--lq-blur,18px)) saturate(var(--lq-sat,190%)) brightness(var(--lq-bright,1.06));
    backdrop-filter:blur(var(--lq-blur,18px)) saturate(var(--lq-sat,190%)) brightness(var(--lq-bright,1.06));
    box-shadow:
      0 1px 2px rgba(21,43,68,.04),
      0 8px 24px rgba(21,43,68,.07),
      0 24px 64px rgba(21,43,68,.09),
      inset 0 1px 0 rgba(255,255,255,.95),
      inset 0 -16px 26px -20px rgba(30,58,92,.34),
      inset 0 16px 30px -22px rgba(255,255,255,1);
    border:none;
    transition:box-shadow .5s var(--ease), transform .45s var(--spring),
               background .4s var(--ease), backdrop-filter .4s var(--ease);
  }

  /* HIGHLIGHT layer - the lens bevel */
  .lq::before,
  .burger::before, .send::before, .pj-close::before, .pj-nav i::before, .pj-plus::before,
  .pj-panel::before, .pr-bio::before{
    content:''; position:absolute; inset:0; border-radius:inherit; z-index:-1;
    pointer-events:none; padding:var(--lq-edge,1.5px);
    background:conic-gradient(from calc(135deg + var(--tilt,0deg)) at 50% 50%,
        rgba(255,255,255,.98) 0deg,  rgba(185,198,212,.20) 68deg,
        rgba(255,255,255,.82) 148deg, rgba(122,46,60,.26) 216deg,
        rgba(255,255,255,.60) 292deg, rgba(255,255,255,.98) 360deg);
    -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite:xor;
    mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite:exclude;
  }

  /* Small controls read as THICKER glass: a crisper rim plus a deeper stack of
     inset highlights. Inset shadows are used rather than a backdrop-filtered
     ring because a filtered pseudo-element is painted at its border box and
     escapes the border-radius, which halos pill shapes. */
  .burger::before, .send::before, .pj-close::before, .pj-nav i::before, .pj-plus::before{
    display:none;
  }
  .burger, .pj-close, .pj-nav i, .pj-plus{
    box-shadow:
      0 1px 2px rgba(21,43,68,.05),
      0 6px 18px rgba(21,43,68,.09),
      0 16px 40px rgba(21,43,68,.10),
      inset 0 1px 0 rgba(255,255,255,.85),
      inset 0 -12px 20px -14px rgba(30,58,92,.26),
      inset 0 12px 22px -16px rgba(255,255,255,.95);
  }

  /* SPECULAR layer - a moving glint that tracks the pointer, and on phones
     the device's own tilt, exactly as Apple ties highlights to the gyroscope */
  .lq::after,
  .burger::after, .send::after, .pj-close::after, .pj-nav i::after, .pj-plus::after,
  .pj-panel::after, .pr-bio::after{
    content:''; position:absolute; inset:0; border-radius:inherit; z-index:-1;
    pointer-events:none; opacity:0; transition:opacity .45s var(--ease);
    background:
      radial-gradient(40% 60% at var(--mx,50%) var(--my,0%),
                      rgba(255,255,255,.70) 0%, rgba(255,255,255,.18) 42%, transparent 70%),
      linear-gradient(180deg, rgba(255,255,255,.42) 0%, transparent 34%);
  }
  .lq:hover::after,
  .burger:hover::after, .send:hover::after,
  .pj-close:hover::after, .pj-nav:hover i::after,
  .pj-panel::after, .pr-bio::after{opacity:1;}

  /* GEL PHYSICS - squash on press, then spring back. the blur deepening on
     press is what makes it feel like soft material rather than a flat button */
  .brand, .burger, .send, .pj-close, .pj-nav i{cursor:pointer;}
  .burger:hover, .send:hover{transform:translateY(-2px);}
  .burger:active, .send:active, .pj-close:active{
    transform:scale(.955); --lq-blur:28px; --lq-bright:1.14;
    transition:transform .12s ease-out;
  }

  /* thicker material on the large panes */
  .pj-panel{--lq-blur:40px; --lq-sat:200%;}
  .pr-bio  {--lq-blur:14px; --lq-sat:185%;}
  .burger,.pj-close,.pj-nav i,.pj-plus{--lq-blur:14px; --lq-sat:200%;}
  .send    {--lq-blur:16px;}

  /* squircles - Apple keeps the continuous-curvature corner */
  @supports (corner-shape:squircle){
    .lq,.burger,.send,.pj-close,.pj-nav i,.pj-panel,.pr-bio{corner-shape:squircle;}
  }

  /* clickable things get the normal pointer cursor */
  .feature,.hs-card{cursor:pointer;}

  /* reveal-on-scroll - opt IN, never opt out.
     .rv elements are visible by default and only start hidden once site.js has
     confirmed it is running by putting .js on <html>. A blocked or broken
     script therefore degrades to a plain, fully readable page rather than
     hiding the stats, the tagline, the phone numbers and the contact form. */
  .js .rv{opacity:0; transform:translateY(42px);
          transition:opacity 1s cubic-bezier(.16,.9,.3,1), transform 1s cubic-bezier(.16,.9,.3,1);}
  .js .rv.in{opacity:1; transform:none;}

  /* ---- blur-to-focus variant ----
     No vertical travel: the point is that it resolves in place, as if pulling
     into focus. Blur is expensive to composite, so the radius is modest and
     the transition is short - and will-change is applied only while an element
     is still out of focus, never left on permanently. */
  .js .rv.rv-blur{opacity:0; transform:none; filter:blur(14px);
                  will-change:filter, opacity;
                  transition:opacity .95s cubic-bezier(.16,.9,.3,1),
                             filter  1.05s cubic-bezier(.16,.9,.3,1);}
  .js .rv.rv-blur.in{opacity:1; filter:blur(0); will-change:auto;}


  /* ============ HEADER ============ */
  /* A solid white bar on every page, so the nav never has to hold its own
     against whatever is behind it - film, photography or plain linen.
     To stop it permanently occupying the top of the screen it slides away as
     soon as you scroll down and returns the moment you scroll up (see the
     header module in site.js, which drives .hdr-away off scroll DIRECTION,
     not position - so the nav is one upward flick away at any depth).

     The opacity transition is here on purpose: the home page's intro
     sequence fades this same element in via .intro-armed / .intro-out, and
     declaring only `transform` would silently drop that fade. */
  header{position:fixed; top:0; left:0; right:0; z-index:900; padding:clamp(16px,2.4vh,26px) 0;
         color:var(--color-charcoal); pointer-events:none;
         background:#fff; box-shadow:0 1px 0 rgba(30,58,92,.09); will-change:transform;
         transition:transform .45s var(--ease), opacity .8s var(--ease) .1s;}
  #hdr.hdr-away{transform:translateY(-100%);}
  @media (prefers-reduced-motion:reduce){
    header{transition:none;}
    #hdr.hdr-away{transform:none;}
  }
  /* three tracks: the mark sits dead-centre on the page, the burger stays right */
  nav{display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:24px;}
  nav > *{pointer-events:auto;}
  .brand{grid-column:2; justify-self:center; display:flex; align-items:center; gap:18px;}
  .nav-act{display:flex; align-items:center; gap:22px;}

  .nav-links{display:flex; align-items:center; gap:clamp(18px,2vw,32px);
             padding:12px 0; background:none;}

  .burger{grid-column:3; justify-self:end; display:grid; gap:6px; padding:17px 19px;
          background:none;}
  .burger i{display:block; width:26px; height:1.5px; border-radius:2px; background:var(--color-charcoal);
            transition:transform .35s, opacity .3s, width .35s;}
  .burger:hover i:nth-child(2){width:22px;}
  body.menu-open .burger i:nth-child(1){transform:translateY(3px) rotate(45deg);}
  body.menu-open .burger i:nth-child(2){opacity:0;}
  body.menu-open .burger i:nth-child(3){transform:translateY(-3px) rotate(-45deg);}

  /* The soft white scrim that used to sit behind the header on the image-led
     pages is gone - the solid bar above does that job outright now. */

  /* ============ MENU - floating glass buttons, no panel ============
     The overlay itself is transparent; each link is its own liquid-glass pill.
     That is why the buttons must carry the glass - with no backdrop behind
     them, plain text would be unreadable over whatever section is showing. */
  .menu{position:fixed; inset:0; z-index:880; display:grid; place-content:center;
        background:none; pointer-events:none;
        opacity:0; visibility:hidden;
        transition:opacity .42s var(--ease), visibility .42s;}
  body.menu-open .menu{opacity:1; visibility:visible; pointer-events:auto;}
  .menu-nav{display:flex; flex-direction:column; gap:clamp(10px,1.4vh,18px);
            align-items:center; padding:0 clamp(20px,5vw,60px);}
  .menu-nav a{display:flex; align-items:center; justify-content:center;
              min-width:min(360px,74vw); padding:clamp(13px,1.7vh,20px) clamp(28px,4vw,52px);
              border-radius:999px; position:relative; isolation:isolate;
              /* the family was already the heading face; what set the menu apart
                 was label styling - uppercase and tracked out. It now takes the
                 heading treatment: sentence case, weight 600, tight tracking. */
              font-family:var(--f-display); font-weight:600; text-transform:none;
              color:var(--color-charcoal); font-size:clamp(17px,2.1vw,29px);
              letter-spacing:var(--ls-head); margin-right:0; line-height:1.2;
              background:linear-gradient(150deg, rgba(255,255,255,.62) 0%,
                                                 rgba(255,255,255,.34) 46%,
                                                 rgba(255,255,255,.56) 100%);
              -webkit-backdrop-filter:blur(16px) saturate(200%) brightness(1.06);
              backdrop-filter:blur(16px) saturate(200%) brightness(1.06);
              box-shadow:0 1px 2px rgba(21,43,68,.05),
                         0 8px 24px rgba(21,43,68,.10),
                         0 22px 56px rgba(21,43,68,.12),
                         inset 0 1px 0 rgba(255,255,255,.92),
                         inset 0 -12px 20px -14px rgba(30,58,92,.28),
                         inset 0 12px 22px -16px rgba(255,255,255,.95);
              /* each pill rises in turn once the overlay is up */
              opacity:0; transform:translateY(16px) scale(.97);
              transition:opacity .5s var(--ease), transform .5s var(--spring),
                         box-shadow .35s var(--ease), background .35s var(--ease);}
  body.menu-open .menu-nav a{opacity:1; transform:none;}
  body.menu-open .menu-nav a:nth-child(1){transition-delay:.06s}
  body.menu-open .menu-nav a:nth-child(2){transition-delay:.12s}
  body.menu-open .menu-nav a:nth-child(3){transition-delay:.18s}
  body.menu-open .menu-nav a:nth-child(4){transition-delay:.24s}
  body.menu-open .menu-nav a:nth-child(5){transition-delay:.30s}
  .menu-nav a:hover{background:linear-gradient(150deg, rgba(255,255,255,.78), rgba(255,255,255,.52));
                    box-shadow:0 2px 6px rgba(21,43,68,.06),
                               0 14px 34px rgba(21,43,68,.14),
                               0 30px 70px rgba(21,43,68,.16),
                               inset 0 1px 0 rgba(255,255,255,1);
                    transform:translateY(-2px);}
  .menu-nav a:active{transform:scale(.97);}
  .menu-nav a:focus-visible{outline:2px solid var(--brass); outline-offset:5px;}
  .menu-nav a span{display:inline-block;}

  /* ============ HERO BAND - one continuous backdrop behind
     hero -> stats. Same pattern as the city bands: the backdrop is absolutely
     positioned across the whole band so it can never paint past the band's
     bounds, and the INNER element is what sticks to the viewport. ============ */
  .hero-band{position:relative; background:var(--bg);}
  .hero-bg-wrap{position:absolute; inset:0; z-index:0; pointer-events:none;}
  .hero-bg-stick{position:sticky; top:0; height:100svh; overflow:hidden;}
  .hero-bg-stick video{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
              will-change:transform; backface-visibility:hidden;
              filter:saturate(1.12) contrast(1.04) brightness(1.0);}
  /* A video element with no decoded frame paints black. If a clip is blocked,
     still loading, or refused autoplay, this shows a pale brand tone under the
     poster instead of a black rectangle. */
  .hero-bg-stick video, .city-bg-stick video, .talk-bg-stick video{background:var(--color-salt);}

  /* ---- photo slideshow: sits in the same spot the video does, starts hidden,
     JS crossfades between slides once the intro film has played out. Same
     object-fit/grade as the video so the handoff doesn't jump.
     The fade is quick - each photo still holds for the same interval
     (--hs-dwell, written by site.js), it just settles fast rather than
     dissolving the whole time it's up. ---- */
  .hero-slides{position:absolute; inset:0; z-index:0; opacity:0;
               transition:opacity 1.1s var(--ease,ease);}
  .hero-slides.is-on{opacity:1;}
  .hs-slide{position:absolute; inset:0; opacity:0; transition:opacity 1.1s ease;}
  .hs-slide picture{display:block; width:100%; height:100%;}
  .hs-slide img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
                display:block; filter:saturate(1.12) contrast(1.04) brightness(1.0);}
  .hs-slide.is-active{opacity:1; z-index:1;}
  /* ---- slideshow scrubber: a thin gray segment per photo, sitting flush
     across the top of the frame from the left edge up to where the caption
     starts (site.js sets the width to match - it varies with how long each
     location's name runs). Starts invisible; JS brings it up once the
     slideshow itself starts, then hands its opacity to the scroll-linked
     fade below (moveHeroBar in site.js) so it clears out of the logo's way.
     Fixed height (rather than padding top/bottom) so the caption beside it
     can centre on the exact same line - see --hs-bar-h below. */
  .hs-bar{--hs-bar-h:56px; position:absolute; top:0; left:0; z-index:3; height:var(--hs-bar-h);
         display:flex; align-items:center; gap:8px; padding:0 clamp(18px,2.6vw,34px); opacity:0;
         pointer-events:none; transition:opacity .6s var(--ease,ease);}
  .hs-bar.is-on{opacity:1; pointer-events:auto;}
  .hs-bar-seg{flex:1 1 0; min-width:16px; height:100%; display:flex; align-items:center;
             background:none; border:0; margin:0; padding:0; cursor:pointer;
             -webkit-tap-highlight-color:transparent;}
  .hs-bar-seg i{display:block; width:100%; height:3px; border-radius:2px;
               background:rgba(158,158,158,.5); transition:background .35s ease;}
  .hs-bar-seg:hover i{background:rgba(158,158,158,.75);}
  .hs-bar-seg.is-active i{background:rgba(90,90,90,.95);}
  .hs-bar-seg.is-active:hover i{background:rgba(90,90,90,.95);}

  /* Small caption, top-right of each photo - same font as the hero's own
     intro line (weight, tracking, sentence case), just sized down and
     brightened so it's legible without competing with it. Centred on the
     scrubber bar's own line (half its height) so the two read as one row. */
  .hs-loc{position:absolute; top:calc(56px / 2); right:clamp(18px,2.6vw,34px); z-index:2;
          transform:translateY(-50%);
          font-family:var(--f-ui); font-weight:400; font-size:12px;
          letter-spacing:.055em; margin-right:-.055em; text-transform:none;
          color:#fff; text-shadow:0 1px 6px rgba(8,16,28,.85), 0 0 16px rgba(8,16,28,.55);}
  /* Only enough wash to hold the wordmark - a soft halo at centre where the
     type sits, near-clear everywhere else, so the footage actually reads.
     Centred at 47% and taller than it looks it needs to be: the tagline sits
     at ~57% of the viewport, and at the old 44%/46% falloff it landed in the
     thin edge of the wash - measured, over half its glyph pixels were under
     4.5:1 against the downtown skyline. */
  /* Promoted to its own layer on purpose. The video underneath is composited
     and is itself scaled on scroll; an unpromoted full-screen gradient sitting
     on top of it has to be repainted into the parent layer every frame, which
     is what made the wash flicker. Its own layer is composited, not repainted. */
  .hero-veil{position:absolute; inset:0; z-index:1; pointer-events:none;
              will-change:transform; transform:translateZ(0);
              background:radial-gradient(64% 56% at 50% 47%, rgba(255,255,255,.88) 0%,
                                         rgba(255,255,255,.66) 38%, rgba(255,255,255,.34) 66%,
                                         rgba(255,255,255,.10) 84%, transparent 100%),
                         linear-gradient(180deg, rgba(255,255,255,.34) 0%, rgba(255,255,255,.05) 26%,
                                         rgba(255,255,255,.05) 58%, rgba(245,248,250,.42) 80%,
                                         rgba(245,248,250,.86) 93%, rgba(245,248,250,1) 100%);}
  .hero, .stats{position:relative; z-index:2;}

  /* ============ HERO - centered wordmark across screen ============ */
  .hero{position:relative; background:transparent;}
  /* minmax(0,1fr) caps the column at the container width. Without it the track
     is auto-sized and grows to fit the wordmark - 20,719px at full zoom - which
     makes .hero-mark that wide too, so every percentage inside it resolves
     against the wrong number and the centring maths silently breaks. */
  .hero-stick{position:sticky; top:0; height:100svh; min-height:600px; overflow:hidden;
              display:grid; grid-template-columns:minmax(0,1fr);
              place-items:center; text-align:center;}
  /* will-change:transform is deliberately NOT set here. The zoom is driven by
     font-size (see site.js), and promoting this element would have Chrome cache
     one giant rasterisation of a mark that is ~21,000px wide at full zoom -
     past the 16,384px texture limit, which is what made letters drop out.

     Absolutely centred rather than laid out by the grid. Because the zoom drives
     font-size, the mark's LAYOUT height grows past the viewport at high zoom, and
     grid/flex centring switches to "safe" alignment there - pinning it to the top,
     which read as the title sliding downwards as it zoomed. top:50% + translateY
     is pure geometry and cannot be clamped, so the wordmark holds its centre the
     whole way. (scale() never hit this: transforms don't affect layout height.) */
  .hero-mark{position:absolute; left:0; right:0; top:50%; transform:translateY(-50%);
             z-index:3; color:var(--color-charcoal); padding:0;
             will-change:opacity; transform-origin:50% 50%; text-align:center;
             display:flex; flex-direction:column; align-items:center;
             backface-visibility:hidden; -webkit-font-smoothing:antialiased;}
  .hero-mark h1, .hero video{backface-visibility:hidden;}
  /* the hero wordmark keeps the geometric sans it was built around */
  /* Centred by explicit geometry, not by an alignment property. Both
     text-align:center and flex align-items:center clamp an item wider than its
     container to the start edge ("safe" alignment), which sent the zoom running
     off to the right instead of growing from the middle of the phrase.
     flex-start fixes the static position at 0 whatever the width, then
     left:50% + translateX(-50%) puts the mark's own centre on the viewport
     centre. translate never rescales the raster, so it costs nothing here. */
  .hero-mark h1{font-family:var(--f-ui); text-transform:uppercase;
                /* Viewport-relative, not a flat 60px. JS overrides this inline via
                   fitHero, so it only shows when JS never runs - and at 60px the
                   wrapped line ran to ~506px on a 390px phone, clipping at both
                   ends now that the mark is centred by geometry. */
                display:inline-block; font-size:min(56px,8vw); font-weight:300; line-height:1.06;
                letter-spacing:0.12em; margin-right:-0.12em; white-space:nowrap;
                align-self:flex-start; position:relative; left:50%;
                transform:translateX(-50%);}
  .hero-mark .sub{margin-top:26px; font-family:var(--f-ui); font-weight:400;
                  font-size:clamp(9px,1vw,11.5px); letter-spacing:.42em; margin-right:-.42em;
                  text-transform:uppercase; color:var(--color-charcoal);}
  .hero-scroll{position:absolute; z-index:3; bottom:34px; left:50%; transform:translateX(-50%);
               font-family:var(--f-ui); font-size:9.5px; letter-spacing:var(--ls-label);
               margin-right:calc(var(--ls-label) * -1); text-transform:uppercase; color:var(--color-charcoal); opacity:.8;}


  /* ============ LOGO ============ */
  .brand-sym{height:clamp(38px,3.8vw,52px); width:auto; object-fit:contain; display:block;
             filter:drop-shadow(0 2px 8px rgba(255,255,255,.85)) drop-shadow(0 1px 4px rgba(21,43,68,.18));
             transition:transform .5s cubic-bezier(.16,.9,.3,1), opacity .3s;}
  .brand:hover .brand-sym{transform:rotate(-8deg) scale(1.05);}

  /* footer horizontal lockup: symbol | wordmark */
  .lockup{display:flex; align-items:center; gap:clamp(12px,1.5vw,20px);}
  .lockup img{height:clamp(38px,4vw,56px); width:auto; object-fit:contain;}
  .lockup .rule{width:1px; align-self:stretch; background:var(--color-bronze); opacity:.55; margin:4px 0;}
  .lockup .words{font-family:var(--f-ui); font-weight:300; text-transform:uppercase; line-height:1.15;}
  .lockup .words .l1{font-size:clamp(15px,2vw,28px); letter-spacing:.22em; margin-right:-.22em; display:block;}
  .lockup .words .l2{font-size:clamp(8px,.8vw,11px); letter-spacing:.42em; margin-right:-.42em;
                     display:block; color:var(--muted-l); margin-top:6px;}

  /* menu overlay mark */

  /* watermark behind the dark tagline band */
  /* small solid mark sitting above the statement */
  .wm-crest{height:clamp(34px,3.4vw,52px); width:auto; object-fit:contain;
            margin:0 auto clamp(20px,2.6vh,30px); display:block; opacity:1;}

  /* principals label mark */
  .pr-label img{height:34px; width:auto; object-fit:contain; opacity:.9; margin:0 auto 14px;}


  /* ---- soft seams: dark sections fade in/out instead of butting hard
         against the light ones ---- */
  .blend{position:relative;}
  .blend:before,.blend:after{content:''; position:absolute; left:0; right:0; height:clamp(60px,9vh,130px);
                             pointer-events:none; z-index:1;}
  .blend:before{top:0;    background:linear-gradient(180deg, rgba(245,248,250,.85), transparent);}
  .blend:after {bottom:0; background:linear-gradient(0deg,   rgba(245,248,250,.85), transparent);}
  .blend > *{position:relative; z-index:2;}

  /* light sections that sit next to a dark one get a matching soft edge */
  .blend-l{position:relative;}
  .blend-l:before,.blend-l:after{content:''; position:absolute; left:0; right:0; height:clamp(50px,7vh,110px);
                                 pointer-events:none; z-index:0;}
  .blend-l:before{top:0;    background:linear-gradient(180deg, rgba(var(--ink-rgb),.05), transparent);}
  .blend-l:after {bottom:0; background:linear-gradient(0deg,   rgba(var(--ink-rgb),.05), transparent);}
  .blend-l > *{position:relative; z-index:1;}

  /* ============ STATS ============
     No card, no block - the numbers sit directly on the hero photo/video,
     tucked close to the bottom of that backdrop. The hero-veil's own dark
     grade (darkest at its bottom edge) is what keeps the white type legible;
     no separate fill is layered in here. */
  .stats{padding:clamp(28px,4vh,48px) 0 clamp(14px,2vh,22px); background:transparent;}
  .stats-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(18px,2.4vw,32px);}
  .stat{padding:0; background:none;}
  .stat h2{font-family:var(--f-display); font-weight:700; font-size:clamp(64px,9.5vw,168px);
           letter-spacing:var(--ls-display); line-height:.98; color:#fff;
           font-variant-numeric:lining-nums tabular-nums;}
  .stat span{display:block; margin-top:18px; font-size:13px; letter-spacing:.28em; margin-right:-.28em;
             text-transform:none; font-weight:500; color:rgba(255,255,255,.72); line-height:1.6;
             font-family:Arial, Helvetica, sans-serif;}

  /* ============ PRINCIPALS - pinned scroll-driven cards ============ */
  .principals{position:relative; background:transparent; color:var(--color-charcoal);}
  .pr-sticky{position:sticky; top:0; height:100svh; overflow:hidden; display:grid; place-items:center;}
  /* highway footage behind both cards */
  .pr-sticky > .pr-label, .pr-sticky > .pr-card, .pr-sticky > .pr-prog{position:absolute; z-index:3;}
  .pr-sticky > .pr-label{position:absolute;}
  .pr-label{position:absolute; top:clamp(80px,12vh,130px); left:0; right:0; text-align:center; z-index:5;
            will-change:opacity,transform; pointer-events:none;}
  .pr-card{position:absolute; inset:0; display:grid; place-items:center;
           padding:0 clamp(20px,5vw,90px); will-change:transform,opacity;}
  .pr-photo{width:clamp(120px,13vw,178px); height:clamp(120px,13vw,178px); flex:0 0 auto;
            border-radius:50%; overflow:hidden; will-change:transform,opacity;
            border:1px solid rgba(255,255,255,.8);
            box-shadow:var(--sh-lg), 0 0 0 1px rgba(30,58,92,.06);}
  .pr-photo img{object-position:50% 18%; filter:grayscale(.14) contrast(1.02) brightness(1);}
  .pr-bio{will-change:transform,opacity; width:min(1080px,100%);
          padding:clamp(28px,3.4vw,54px) clamp(26px,3.6vw,60px);
          border-radius:var(--r-xl); transform:translateZ(0);}
  .pr-name,.pr-body{will-change:transform;}
  .pr-head{display:flex; align-items:center; gap:clamp(18px,2.4vw,34px); margin-bottom:clamp(20px,2.6vh,32px);}
  .pr-bio .role{display:block; font-size:10px; letter-spacing:4px; text-transform:uppercase;
                color:var(--green); font-weight:500; margin-bottom:10px;}
  .pr-bio h3{font-size:clamp(28px,3.6vw,52px); font-weight:600; margin:0; color:var(--color-charcoal);}
  .pr-body{display:grid; grid-template-columns:1.25fr 1fr; gap:clamp(22px,3vw,52px); align-items:start;}
  .pr-bio p{font-size:14.5px; color:var(--muted-l); max-width:52ch; margin-bottom:14px;}
  .pr-bio .facts{display:grid; gap:12px; align-content:start;}
  .pr-bio .facts div{font-size:13px; color:var(--muted-l); line-height:1.5;}
  .pr-bio .facts b{display:block; color:var(--green); font-weight:500; font-size:9.5px; letter-spacing:2.5px;
                   text-transform:uppercase; margin:0 0 3px;}
  .pr-prog{position:absolute; bottom:clamp(30px,5vh,58px); left:50%; transform:translateX(-50%);
           display:flex; gap:10px; z-index:5;}
  .pr-prog i{width:46px; height:2px; border-radius:2px; background:rgba(30,58,92,.14); display:block; overflow:hidden;}
  .pr-prog i b{display:block; height:100%; width:0; background:var(--brass);}

  /* ============ LEAD / TAGLINE ============ */
  /* ---- STICKY REVEAL ----
     The tagline panel pins to the top of the viewport and the client rail
     rides up over it. The rail needs an OPAQUE background and a higher stacking
     order, otherwise it would scroll past transparently and reveal nothing. */
  .tagline-sec{position:relative; padding:clamp(90px,16vh,190px) 0;
               min-height:100svh; display:grid; align-content:center;}
  .tagline-sec .wrap{text-align:center;}
  .tagline-sec h2{font-size:clamp(28px,4.6vw,76px); font-weight:600; letter-spacing:0.2px; line-height:1.14;
                  max-width:22ch; margin:22px auto 0;}
  /* the emphasised clause: italic serif in cashmere */
  .tagline-sec h2 em{font-style:italic; font-weight:500; color:var(--brass);}
  .tagline-sec .tag{display:inline-block;}
  /* localised darkening behind the text column only */
  .tagline-sec .wrap:before{content:''; position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
      width:min(92%,1100px); height:160%; z-index:-1; pointer-events:none;
      background:radial-gradient(60% 55% at 50% 50%, rgba(255,255,255,.72) 0%, transparent 74%);}
  .tagline-sec .cta{margin-top:clamp(30px,4vh,46px); display:inline-flex; align-items:center; gap:14px; font-family:var(--f-ui);
                    font-size:10.5px; letter-spacing:.3em; font-weight:500; text-transform:uppercase; padding-bottom:10px;
                    border-bottom:1px solid var(--color-bronze); transition:gap .35s, color .35s;}
  .tagline-sec .cta:hover{gap:26px; color:var(--brass);}
  .lead-grid{display:grid; grid-template-columns:repeat(12,1fr); gap:26px;}
  .lead-grid p{grid-column:1 / span 8; font-family:var(--f-ui); font-weight:300; text-transform:uppercase;
               font-size:clamp(17px,2.2vw,32px); line-height:1.5; letter-spacing:.1em; margin-right:-.1em;}

  /* ============ FEATURED PROJECT ============ */
  /* Top eased back - the title sits right under "What we do" now rather than
     floating in a lot of open white. Bottom kept generous, that gap is doing
     a different job: separating this whole card from Contact below. */
  .feature-sec{padding:clamp(32px,5vh,64px) 0 clamp(70px,11vh,140px); background:#fff; color:var(--color-charcoal);}
  /* .feat-head is shared with the "team" kicker on the About page - this is
     its default, small-and-centred form. The Star card overrides it below,
     scoped to .feature-grid, so About stays untouched. */
  .feat-head{position:sticky; top:clamp(68px,9.5vh,104px); z-index:4;
             text-align:center; padding:10px 0 18px;
             margin-bottom:clamp(28px,4vh,54px);}
  .feat-head h2{font-size:clamp(26px,3.4vw,52px); font-weight:600;
                text-shadow:0 1px 10px rgba(255,255,255,.9),
                            0 0 26px rgba(255,255,255,.75),
                            0 0 52px rgba(255,255,255,.5);}
  .feature{backface-visibility:hidden;}
  .feature-sec h3{color:var(--ink);}
  .feature-sec .specs div p{color:var(--color-midnight); font-weight:700;}

  /* Stats on the left over plain white, photo pinned to the right and run
     out to the true edge of the window - the card lives outside .wrap now
     (see index.html), so only the stats column keeps the page's left
     margin; the grid itself carries no padding of its own. The title now
     lives in this same grid, in the image's column, so a row is added
     above it purely for the heading. */
  /* Row-gap kept tight - just enough air between the title and the photo -
     column-gap stays wide, that is the space between the stats and the
     image and needs the room. The stats column is short next to the photo,
     so it's vertically centred alongside it rather than pinned to the top. */
  .feature-grid{display:grid; grid-template-columns:minmax(240px,0.5fr) 1.5fr;
                grid-template-areas:". head" "info media";
                gap:clamp(10px,1.4vh,18px) clamp(32px,4.4vw,64px); align-items:center;}
  .feature-info{grid-area:info;}
  .feature-media{grid-area:media;}

  /* The Star card's own title: sits in the image's grid column, written
     directly above it - a magazine-title treatment, left-set and run big,
     rather than the small centred kicker .feat-head is everywhere else.
     Living in the same column as .feature-media (grid-area:head, same
     grid) is what keeps it lined up exactly with the photo instead of
     guessing at .wrap's width. */
  .feature-grid .feat-head{grid-area:head; position:static; z-index:auto;
                           text-align:left; padding:0; margin-bottom:0;}
  /* Stretched slightly taller than its natural proportions - a condensed,
     editorial-poster feel. Anchored to the baseline (0 100%) so it grows
     upward into the space above rather than pushing the photo down. */
  .feature-grid .feat-head h2{font-size:clamp(44px,6.6vw,108px); font-weight:700;
                              line-height:.96; letter-spacing:-.01em; text-shadow:none;
                              transform:scaleY(1.16); transform-origin:0 100%;}

  /* Square corners, no shadow - flush with the edge of the window rather
     than reading as a floating card. */
  .feature-media{position:relative; height:clamp(360px,78vh,760px); overflow:hidden;}
  .feature-media picture{display:block; width:100%; height:100%;}
  /* Oversized and pulled up by the same margin on both edges, so JS can
     drift it up/down inside the frame without ever showing a gap - the
     frame (and everything outside it: title overlay, stats, whitespace)
     stays put; only the photo itself has the independent motion. */
  /* Brighter grade, lighter grayscale - the photo reads clearly on its own
     now rather than leaning on the scrim to make up for a dim source frame. */
  .feature-media img{position:absolute; top:-12%; left:0; width:100%; height:124%;
                     filter:grayscale(.05) contrast(1.02) brightness(1.18);}

  /* Dark grade at the foot of the photo only, just enough to hold the white
     title - a real layer rather than a text-shadow, so it reads honestly
     under measurement rather than relying on a glow that only helps some
     photos. Nudged up slightly to cover the brighter photo above. */
  /* Darkest through the middle band, where the title now sits, so it stays
     legible against whatever part of the photograph is behind it. */
  .feature-scrim{position:absolute; inset:0; z-index:1; pointer-events:none;
                 background:linear-gradient(180deg, rgba(8,16,28,.12) 0%,
                                            rgba(8,16,28,.44) 36%, rgba(8,16,28,.50) 64%,
                                            rgba(8,16,28,.16) 100%);}

  /* The title lives on the photograph, static, centred top-to-bottom -
     "The Star" left, the location right. Regular weight, not bold. */
  .feature-overlay{position:absolute; inset:0; z-index:2;
                   display:flex; align-items:center; justify-content:space-between;
                   padding:clamp(22px,3.4vw,48px); pointer-events:none;}
  .feature-overlay h3{color:#fff; font-family:Georgia,'Times New Roman',serif;
                      font-size:clamp(26px,3.4vw,52px); font-weight:400;
                      text-shadow:0 2px 20px rgba(8,16,28,.5);}
  .feature-overlay .loc{color:#fff; font-family:Georgia,'Times New Roman',serif;
                        font-size:clamp(26px,3.4vw,52px); font-weight:400; letter-spacing:0; margin-right:0;
                        text-transform:none; opacity:.92;
                        text-shadow:0 2px 20px rgba(8,16,28,.5);}

  /* Left column: three facts stacked, each rule-topped, no labels - the
     value itself is written as a full phrase now, sized up and pushed to
     the font's max weight so the column reads as bold at a glance. */
  .feature-info{padding-top:0; padding-left:clamp(20px,4vw,64px);}
  .specs{display:grid; grid-template-columns:1fr;}
  .specs div{padding:26px 0; border-top:1px solid rgba(30,58,92,.14);}
  .specs div:first-child{border-top:none; padding-top:0;}
  .specs div p{font-size:clamp(26px,3vw,38px); font-weight:700; line-height:1.18;
              letter-spacing:-.015em; font-family:var(--f-display); color:var(--color-midnight);}

  /* ============ HORIZONTAL SCROLL PROJECTS ============ */
  .hs{position:relative; background:#fff; color:var(--color-charcoal);}
  .hs-inner{position:sticky; top:0; height:100svh; overflow:hidden;
            display:flex; flex-direction:column; justify-content:center;
            padding:clamp(76px,11vh,120px) 0 clamp(66px,10vh,104px);}
  /* heading centred across the top of the section */
  .hs-head{position:absolute; top:clamp(74px,10.5vh,116px); left:0; right:0; z-index:4;
           padding:0 clamp(20px,4vw,64px); display:flex; flex-direction:column; align-items:center; gap:16px;}
  .hs-head h2{font-size:clamp(26px,3.4vw,52px); font-weight:600; color:var(--color-charcoal); text-align:center;}
  .hs-prog{width:min(240px,38vw); height:2px; border-radius:2px; background:rgba(30,58,92,.14); position:relative; margin-top:18px;}
  .hs-prog i{position:absolute; left:0; top:0; height:100%; width:0; border-radius:2px; background:var(--brass);}
  .hs-track{--card-h:clamp(300px,46vh,480px);
            display:flex; gap:clamp(14px,1.8vw,26px); padding:0 clamp(20px,4vw,64px); will-change:transform;}
  .hs-card{flex:0 0 clamp(270px,30vw,480px);}
  /* every card shares one media height; a card carrying --ar sizes its width
     from that height x the image's own aspect, so nothing is cropped or matted */
  .hs-card[style*="--ar"]{flex:0 0 calc(var(--card-h) * var(--ar));}
  .hs-card[style*="--ar"] img{object-fit:cover; object-position:50% 50%;}
  .hs-card .m{position:relative; height:var(--card-h); overflow:hidden;
              border-radius:var(--r-md); box-shadow:var(--sh-md);
              transition:box-shadow .55s var(--ease), transform .55s var(--ease);}
  .hs-card:hover .m{box-shadow:var(--sh-lg); transform:translateY(-6px);}
  .hs-card .m picture{display:block; width:100%; height:100%;}
  .hs-card img{filter:grayscale(.15) contrast(1.02) brightness(.94); transition:transform 1.1s cubic-bezier(.16,.9,.3,1), filter .5s;}
  .hs-card:hover img{transform:scale(1.06); filter:grayscale(.12) contrast(1.02) brightness(1);}
  .hs-card .info{padding-top:20px;}
  .hs-card .info span{display:block; font-size:9.5px; letter-spacing:3px; text-transform:uppercase; color:var(--green); margin-bottom:8px;}
  .hs-card .info h3{font-size:clamp(16px,1.5vw,22px); color:var(--color-charcoal);}
  .hs-card .info p{font-size:12.5px; color:var(--muted-l); margin-top:6px;}
  .hs-note{flex:0 0 clamp(210px,20vw,300px); display:flex; align-items:center;}
  .hs-note p{font-size:14px; color:var(--muted-l);}

  /* ============ SERVICES - segmented selector ============
     A heading, one thin rule divided into five clickable segments, and a panel
     that swaps beneath. Panels share a single grid cell so the container is as
     tall as the tallest one - otherwise the page would jump every time a
     visitor picked a different service.                                    */
  .svc{position:relative; padding:clamp(72px,12vh,150px) 0 clamp(64px,10vh,120px);
       background:transparent;}
  .svc-title{font-size:clamp(30px,4.4vw,68px); font-weight:600;
             letter-spacing:var(--ls-display); line-height:var(--lh-display);
             margin:0 0 clamp(38px,6vh,72px);}

  .svc-bar{display:grid; grid-template-columns:repeat(5,1fr); gap:clamp(6px,1vw,16px);
           margin-bottom:clamp(38px,6vh,70px);}
  .svc-seg{background:none; border:0; padding:0; cursor:pointer; text-align:left;
           display:flex; flex-direction:column; gap:12px; border-radius:0;
           min-height:52px; justify-content:flex-end;
           -webkit-appearance:none; appearance:none;}
  .svc-seg-n{font-family:var(--f-ui); font-size:9.5px; font-weight:500;
             letter-spacing:.3em; color:var(--muted-l); transition:color .3s var(--ease);}
  .svc-seg-name{font-family:var(--f-display); font-weight:500;
                font-size:clamp(12px,1.05vw,16px); line-height:1.35;
                color:var(--muted-l); transition:color .3s var(--ease);}
  /* the divided rule itself */
  .svc-seg i{display:block; height:3px; border-radius:2px; margin-top:4px;
             background:rgba(30,58,92,.16);
             transition:background .35s var(--ease), transform .35s var(--ease);
             transform-origin:0 50%;}
  .svc-seg:hover .svc-seg-name,
  .svc-seg:hover .svc-seg-n{color:var(--color-charcoal);}
  .svc-seg:hover i{background:rgba(30,58,92,.34);}
  .svc-seg.is-on .svc-seg-name{color:var(--color-charcoal);}
  .svc-seg.is-on .svc-seg-n{color:var(--brass);}
  .svc-seg.is-on i{background:var(--brass); transform:scaleY(1.7);}
  .svc-seg:focus-visible{outline:2px solid var(--brass); outline-offset:6px;}

  .svc-panels{display:grid;}
  .svc-panel{grid-area:1 / 1; display:grid; grid-template-columns:1.02fr .98fr;
             gap:clamp(24px,5vw,80px); align-items:center;
             opacity:0; visibility:hidden; transform:translateY(16px);
             transition:opacity .5s var(--ease), transform .5s var(--ease), visibility .5s;}
  .svc-panel.is-on{opacity:1; visibility:visible; transform:none;}
  .svc-panel[hidden]{display:grid;}   /* keep it in the grid so heights match */

  .svc-txt .n{display:block; font-family:var(--f-ui); font-size:11px;
              letter-spacing:var(--ls-label); font-weight:500;
              color:var(--brass); margin-bottom:clamp(14px,2.2vh,26px);}
  .svc-txt h3{margin-top:0; font-size:clamp(28px,4vw,60px); font-weight:600; line-height:1.06;
              letter-spacing:var(--ls-head); color:var(--dark);}
  .svc-txt p{margin-top:clamp(16px,2.6vh,28px); font-size:clamp(14px,1.15vw,17px);
             max-width:46ch; color:var(--muted-l);}

  .svc-shot{position:relative; height:clamp(260px,50vh,520px); overflow:hidden;
            border-radius:var(--r-lg); box-shadow:var(--sh-xl);}
  .svc-shot picture{display:block; width:100%; height:100%;}
  .svc-shot img{filter:grayscale(.12) contrast(1.02) brightness(1);}
  .svc-shot:after{content:''; position:absolute; inset:0; pointer-events:none;
                  background:linear-gradient(180deg, transparent 38%,
                                             rgba(21,43,68,.34) 62%,
                                             rgba(21,43,68,.86) 100%);
                  border-radius:inherit;}
  /* The caption carries its OWN scrim rather than relying on the image
     gradient, so contrast is identical no matter how bright the photograph
     underneath happens to be. */
  .svc-cap{position:absolute; left:0; right:0; bottom:0; z-index:2;
           padding:clamp(46px,6vw,80px) clamp(18px,2.2vw,30px) clamp(18px,2.2vw,30px);
           background:linear-gradient(180deg, transparent 0%,
                                      rgba(21,43,68,.66) 42%,
                                      rgba(21,43,68,.90) 100%);
           border-radius:0 0 var(--r-lg) var(--r-lg);}
  .svc-cap span{display:block; font-size:10px; letter-spacing:3.2px; text-transform:uppercase;
                font-weight:500; color:rgba(255,255,255,.92); margin-bottom:8px;
                text-shadow:0 1px 3px rgba(0,0,0,.5);}
  .svc-cap h4{font-size:clamp(15px,1.5vw,21px); color:#fff; line-height:1.35;
              text-shadow:0 1px 4px rgba(0,0,0,.55);}

  /* ============ TALK BAND - one backdrop behind services + contact ============
     Same construction as the city bands: the backdrop is absolutely positioned
     across the whole band so it can never paint past it, and the inner element
     is what sticks. This is what makes the two sections read as one space. */
  .talk-band{position:relative; background:var(--bg);}
  .talk-bg-wrap{position:absolute; inset:0; z-index:0; pointer-events:none;}
  .talk-bg-stick{position:sticky; top:0; height:100svh; overflow:hidden;}
  .talk-band > section{position:relative; z-index:2;}
  /* NOTE: .talk-band / .contact / .contact-bg / .contact-scrim / .c-grid /
     .c-info / .c-form above are still load-bearing for services.html's own
     contact section - do not remove them. index.html's contact section now
     uses the pinned sequence below instead. */


  /* ============ LET'S TALK - pinned scroll sequence (index.html only) ============
     A tall scroll runway pins one viewport-height stage. The Stockyards photo
     fills it and holds still; three things then animate ON it, each scrubbed
     directly off scroll position (see moveTalkSeq in site.js) so they track
     the wheel rather than playing on a timer:
       1. .talk-h    - "Let's talk" descends from above the top edge, in white
       2. .talk-card - a tall white card rises straight up from the bottom
                       right, carrying the form. Copy inside reads maroon.
       3. .talk-info - a smaller navy card rises straight up afterwards and
                       comes to rest over the card's bottom-left corner, with
                       Robert and Jim's details in white.
     All three start outside the stage's own bounds and are clipped by its
     overflow:hidden, so nothing is ever visible before its turn - no need to
     fade anything in, which is what keeps it reading as travel rather than a
     pop. */
  .talk-seq{position:relative; background:var(--color-charcoal);}
  .talk-seq-pin{position:sticky; top:0; height:100svh; overflow:hidden;
                background:var(--color-charcoal);}

  /* -- the photo: fills the stage and never moves, so the pin reads as the
        camera stopping rather than the image sliding -- */
  /* overflow:hidden so the slightly oversized image below is always clipped to
     the frame - on mobile the pin itself is overflow:visible, so without this
     the scaled photo would push the page sideways */
  .talk-bg{position:absolute; inset:0; z-index:1; overflow:hidden;}
  .talk-bg picture{position:absolute; inset:0; display:block;}
  /* Scaled a few percent because the photo is blurred as it comes into view
     (see moveTalkSeq): a blur samples past the edges of its own element, so
     an exactly-fitted image feathers away to nothing along all four sides.
     Oversizing pushes that soft margin outside the visible frame. */
  .talk-bg img{width:100%; height:100%; object-fit:cover; object-position:50% 50%;
               display:block; transform:scale(1.06); will-change:filter;}
  /* a light overall dim - enough that white wording holds anywhere on the
     frame, not so much that the lamp glow down the corridor is lost */
  .talk-bg-dim{position:absolute; inset:0; background:rgba(12,20,30,.42);}
  /* Photo credit line, tucked into the bottom-left corner the card leaves
     open. Same treatment as the hero slideshow's location captions. */
  /* Top-right corner, tucked close to the section's own top edge. Note this
     sits inside the band the fixed header occupies: the header is hidden
     while you scroll down into this section, so it is clear in normal use,
     but scrolling back up brings the nav over it. */
  .talk-loc{position:absolute; right:clamp(18px,2.6vw,34px);
            top:clamp(20px,3.2vh,36px); z-index:2; pointer-events:none;
            display:inline-flex; align-items:center; gap:6px;
            font-family:var(--f-ui); font-weight:400; font-size:12px;
            letter-spacing:.055em; margin-right:-.055em;
            color:#C3C9D0; text-shadow:0 1px 6px rgba(8,16,28,.85), 0 0 16px rgba(8,16,28,.55);}
  /* currentColor, so the pin tracks the caption's colour automatically. It
     needs its own shadow - text-shadow doesn't reach an inline SVG. */
  .talk-loc svg{width:13px; height:13px; flex:none; fill:currentColor;
            transform:translateY(-.5px);
            filter:drop-shadow(0 1px 4px rgba(8,16,28,.85));}

  /* Sits directly on top of the card, nearly touching it, so the two read as
     one stacked unit. The card's height changes with the viewport, so the
     exact offset is set from JS (sizeTalkSeq) once the card can be measured -
     the value here is only the pre-script fallback. */
  .talk-h{position:absolute; top:auto; bottom:62vh; left:0; right:0; z-index:2; margin:0;
          text-align:center; pointer-events:none; color:#fff;
          font-family:var(--f-display); font-weight:600; line-height:1;
          font-size:clamp(50px,9.6vw,168px); letter-spacing:var(--ls-head);
          text-shadow:0 8px 40px rgba(0,0,0,.45);
          will-change:transform;}
  .talk-h em{font-style:italic; font-weight:500; color:#fff;}

  /* -- the white form card: a square-cornered rectangle centred across the
        stage and sitting flush against its bottom edge, travelling straight
        up and down only (no horizontal drift) -- */
  .talk-card{position:absolute; z-index:3; left:50%;
             margin-left:calc(min(920px,92vw) / -2);
             bottom:0; width:min(920px,92vw);
             max-height:min(76svh,720px); overflow-y:auto;
             background:#fff; border-radius:0;
             box-shadow:0 -18px 70px rgba(0,0,0,.42);
             padding:clamp(24px,2.4vw,40px);}
  /* the card's only heading now, so it carries the space the h3 used to */
  .talk-card .tag{color:var(--brass); margin-bottom:clamp(18px,2.2vh,26px);}
  /* This card is a fixed box on a pinned stage, so the form has to fit the
     space rather than set it - spacing is tighter here than the same form
     gets when it has a whole page section to breathe in. The card is also
     wide now, so the short fields pair up two-across instead of running
     down one long column and leaving the right half empty. */
  .talk-card .c-form{display:grid; grid-template-columns:1fr 1fr;
             column-gap:clamp(14px,1.5vw,22px); margin:0;}
  .talk-card .c-form input,
  .talk-card .c-form textarea{padding:13px 15px; margin-bottom:12px;}
  .talk-card .c-form #cfCompany,
  .talk-card .c-form textarea,
  .talk-card .c-form .send,
  .talk-card .c-form .f-status{grid-column:1 / -1;}
  .talk-card .c-form textarea{margin-bottom:16px;}
  .talk-card .send{padding:15px 40px; justify-self:end;}
  .talk-card .f-status{margin-top:12px;}
  .talk-card .f-status:empty{margin:0;}

  /* -- principals' details, closing out the bottom of the form -- */
  .talk-info{display:flex; flex-wrap:wrap; gap:0 clamp(26px,4vw,64px);
             margin-top:clamp(18px,2.2vh,26px); padding-top:clamp(16px,2vh,24px);
             border-top:1px solid var(--color-border);}
  .talk-info .row{padding:4px 0;}
  .talk-info .row b{display:block; font-family:var(--f-ui); font-size:9.5px;
             letter-spacing:.24em; text-transform:uppercase; font-weight:500;
             color:var(--brass); margin-bottom:3px;}
  .talk-info .row p{font-size:14.5px; line-height:1.5;
             color:var(--color-charcoal); margin:0;}
  .talk-info .row a{color:var(--color-charcoal);
             border-bottom:1px solid rgba(122,46,60,.32); transition:border-color .3s;}
  .talk-info .row a:hover{border-color:var(--brass);}
  /* the card is opaque white, so the form's frosted-glass treatment has
     nothing to refract - solid fields on a hairline border read cleaner */
  .talk-card .c-form input,
  .talk-card .c-form textarea{background:#fff; border:1px solid var(--color-border);
             -webkit-backdrop-filter:none; backdrop-filter:none; box-shadow:none;
             color:var(--brass);}
  /* The visible labels are the placeholders themselves - the real <label>s are
     screen-reader only - so these are darker than a decorative placeholder
     would be, to stay above 4.5:1 while the field is still empty. */
  .talk-card .c-form input::placeholder,
  .talk-card .c-form textarea::placeholder{color:rgba(122,46,60,.78);}
  .talk-card .c-form input:focus,
  .talk-card .c-form textarea:focus{border-color:var(--brass); background:#fff;
             box-shadow:0 0 0 3px rgba(122,46,60,.1);}
  /* room at the bottom of the card for the navy plate that lands over it, so
     the plate never covers the Send button or the status message */
  .talk-card .send{background:var(--brass); margin-left:auto; align-self:flex-end;}
  .talk-card .f-status{text-align:right;}

  @media (max-width:1024px){
    /* The sequence is built around a fixed-height stage and cards sized in
       vw - neither survives a narrow, reflowing layout, and a pinned runway
       on a phone just means a lot of thumb travel. So below this width it
       becomes a plain stacked section: photo, heading over it, then the two
       cards in normal flow. */
    .talk-seq{background:var(--color-charcoal);}
    .talk-seq-pin{position:relative; height:auto; overflow:visible;
                  padding:0 0 clamp(48px,8vh,84px);}
    .talk-bg{position:relative; inset:auto; height:52vh; min-height:300px;}
    /* the section isn't pinned here, so the caption scrolls with the photo and
       has no fixed header to clear - it just sits in the photo's corner */
    .talk-loc{top:clamp(14px,3vh,22px);}
    .talk-h{position:relative; top:auto; bottom:auto; margin:0 auto;
            padding:clamp(30px,6vh,54px) 16px clamp(22px,4vh,36px);
            font-size:clamp(44px,13vw,86px); transform:none !important;}
    .talk-card{position:relative; left:auto; right:auto; bottom:auto; inset:auto;
               width:calc(100% - 32px); max-width:600px; max-height:none;
               overflow:visible; margin:0 auto; transform:none !important;}
    /* one column: paired fields would be too narrow to type into here */
    .talk-card .c-form{grid-template-columns:1fr;}
    .talk-info{gap:0;}
    .talk-info .row{width:100%;}
  }


  /* ============ CITY BAND - one continuous backdrop behind
     principals -> tagline -> featured -> projects rail ============ */
  .city-band{position:relative; background:var(--bg); color:var(--color-charcoal);}
  /* backdrop is absolutely positioned across the band, so it can never paint
     past the band's own bounds; the inner element is what sticks to the viewport */
  .city-bg-wrap{position:absolute; inset:0; z-index:0; pointer-events:none;}
  .city-bg-stick{position:sticky; top:0; height:100svh; overflow:hidden;}
  .city-bg{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
           filter:grayscale(.28) contrast(.98) brightness(1.12);}
  /* frosted pane, not a tint - the footage stays legible but recedes */
  .city-veil{position:absolute; inset:0; pointer-events:none;
             background:rgba(245,248,250,.72);}
  /* feathered top/bottom edges so the bands dissolve into the adjacent
     sections instead of butting against them with a hard seam */
  .city-bg-stick:after{content:''; position:absolute; inset:0; z-index:2; pointer-events:none;
             background:linear-gradient(180deg, rgba(245,248,250,.96) 0%, transparent 20%,
                                                transparent 80%, rgba(245,248,250,.96) 100%);}
  .city-band > *:not(.city-bg-wrap){position:relative; z-index:2;}
  /* must out-specify the rule above, which would otherwise force position:relative */
  .city-band > .tagline-sec{position:sticky; top:0; z-index:2;}
  /* the featured project is now the panel that rides up over the pinned
     tagline; it must be OPAQUE or the reveal shows nothing */
  .city-band > .feature-sec{position:relative; z-index:3;
                   background:#fff;
                   border-radius:var(--r-xl) var(--r-xl) 0 0;
                   box-shadow:0 -20px 56px rgba(21,43,68,.14);
                   padding-top:clamp(64px,9vh,110px);}
  /* the pinned panel fades slightly as it is covered, so the eye follows the
     incoming panel rather than reading two competing layers */
  .city-band > .tagline-sec .wrap{transition:opacity .3s linear;}

  @media (max-width:900px){
    .city-band > .tagline-sec{position:relative; min-height:0;}
    .city-band > .feature-sec{border-radius:0; box-shadow:none;}
  }
  /* The bios and the tagline sit ON the video, so they must paint NO background
     of their own. White begins at the reveal panel (.feature-sec) and continues
     through the projects rail. */
  .city-band .principals,
  .city-band .tagline-sec{background:transparent;}
  .city-band .hs{background:#fff;}
  /* the old per-section seams would band the continuous video - drop them */
  .city-band .blend:before, .city-band .blend:after{display:none;}


  /* ============ PROJECT LIGHTBOX ============ */
  .pj-modal{position:fixed; inset:0; z-index:1200; display:none;}
  .pj-modal.open{display:block;}
  .pj-scrim{position:absolute; inset:0; background:rgba(226,235,241,.62);
            -webkit-backdrop-filter:var(--glass-blur-lg); backdrop-filter:var(--glass-blur-lg);
            opacity:0; transition:opacity .45s ease;}
  .pj-modal.in .pj-scrim{opacity:1;}
  .pj-shell{position:absolute; inset:0; overflow-y:auto; display:flex; align-items:flex-start;
            justify-content:center; padding:clamp(56px,8vh,90px) clamp(16px,4vw,48px);}
  .pj-panel{width:min(1120px,100%); opacity:0; transform:translateY(24px);
            border-radius:var(--r-xl); overflow:hidden;
            transition:opacity .5s cubic-bezier(.16,.9,.3,1), transform .5s cubic-bezier(.16,.9,.3,1);}
  .pj-modal.in .pj-panel{opacity:1; transform:none;}
  /* above .pj-nav (z-index 3): the next-image button spans the whole right
     half of the stage, and at equal z-index it comes later in the DOM, so it
     was swallowing every click on the close button */
  .pj-close{position:absolute; top:14px; right:14px; z-index:6; width:44px; height:44px;
            border-radius:50%; color:var(--color-charcoal);
            font-size:18px; line-height:1; cursor:pointer; transition:background .3s, border-color .3s;}
  .pj-close:hover{--lq-bright:1.16;}
  .pj-media{width:100%;}
  .pj-body{padding:clamp(26px,3.4vw,52px);}
  .pj-body .cat{display:block; font-family:var(--f-ui); font-size:9.5px; letter-spacing:var(--ls-label);
                text-transform:uppercase; color:var(--color-bronze); margin-bottom:12px;}
  .pj-body h3{font-size:clamp(28px,3.6vw,52px); font-weight:600; margin-bottom:6px;}
  .pj-body .loc{font-family:var(--f-ui); font-size:11px; letter-spacing:.24em;
                text-transform:uppercase; color:var(--color-text-secondary);}
  .pj-facts{display:grid; grid-template-columns:repeat(4,1fr); gap:clamp(16px,2.4vw,34px);
            margin:clamp(24px,3vh,38px) 0; padding-top:clamp(20px,2.6vh,30px);
            border-top:1px solid var(--color-border);}
  .pj-facts div b{display:block; font-family:var(--f-ui); font-size:9.5px; letter-spacing:.24em;
                  text-transform:uppercase; color:var(--color-bronze); font-weight:500; margin-bottom:6px;}
  .pj-facts div p{font-family:var(--f-text); font-size:15px; color:var(--color-charcoal); line-height:1.6;}
  .pj-impact h4{font-family:var(--f-ui); font-size:9.5px; letter-spacing:.24em; text-transform:uppercase;
                color:var(--color-bronze); font-weight:500; margin-bottom:10px;}
  .pj-impact p{font-family:var(--f-text); font-size:15.5px; color:var(--color-text-secondary);
               max-width:76ch; margin-bottom:12px;}
  body.pj-open{overflow:hidden;}
  @media(max-width:760px){
    .pj-facts{grid-template-columns:1fr 1fr;}

  }


  /* lightbox gallery */
  /* EVERY slide is absolutely positioned, so changing which one is showing
     never alters layout. The stage's shape is driven solely by aspect-ratio,
     which can transition smoothly on its own.

     Previously the active slide sat in flow and the rest were absolute, so
     switching images swapped which element defined the stage's height - the
     box jumped to the new size instantly while the crossfade and the
     aspect-ratio transition were still running, and the outgoing image
     stretched as it faded. That was the glitch. */
  .pj-stage{position:relative; overflow:hidden; line-height:0;
            aspect-ratio:3 / 2;                 /* only until JS sets an explicit height */
            /* HEIGHT is animated, not aspect-ratio. Interpolating a ratio changes
               height on a reciprocal curve - visually it lurches - and the old
               fast-out easing made that worse. A plain height transition on a
               symmetric curve moves the frame evenly. */
            transition:height .5s cubic-bezier(.4, 0, .2, 1);}
  .pj-stage picture{position:absolute; inset:0; display:block;}
  .pj-stage picture.on{z-index:1;}
  .pj-stage img{width:100%; height:100%; object-fit:cover; display:block;
                opacity:0; transition:opacity .55s ease;}
  .pj-stage picture.on img{opacity:1;}
  /* whole left / right halves are the click targets */
  .pj-nav{position:absolute; top:0; bottom:0; width:50%; z-index:3; border:0; padding:0;
          background:none; cursor:pointer; appearance:none;}
  .pj-nav.prev{left:0;} .pj-nav.next{right:0;}
  .pj-nav i{position:absolute; top:50%; transform:translateY(-50%);
            width:42px; height:42px; display:grid; place-items:center; font-style:normal;
            font-size:15px; border-radius:50%; color:var(--color-charcoal); opacity:0;
            transition:opacity .3s ease, background .3s;}
  .pj-nav.prev i{left:16px;} .pj-nav.next i{right:16px;}
  .pj-nav:hover i{opacity:1;}
  .pj-nav:hover i{--lq-bright:1.16;}
  /* dots */
  .pj-dots{position:absolute; left:0; right:0; bottom:16px; z-index:4;
           display:flex; justify-content:center; gap:9px; pointer-events:none;}
  .pj-dots button{pointer-events:auto; width:8px; height:8px; padding:0; border-radius:50%;
                  border:1px solid rgba(245,248,250,.75); background:none; cursor:pointer;
                  transition:background .3s, border-color .3s, transform .3s;}
  .pj-dots button.on{background:var(--color-ivory); transform:scale(1.15);}
  .pj-dots button:hover{border-color:var(--color-bronze);}


  /* ============ MARQUEE ============ */
  /* ============ CLIENTS - plain grid ============
     Ten names divide cleanly into 5x2 and 2x5, so those are the only two
     column counts used. Three or four columns would leave an orphan on the
     last row, which reads as a mistake rather than a layout.              */
  .mq{padding:clamp(46px,7vh,84px) 0 clamp(52px,8vh,92px); background:transparent;}
  /* Combined selector, not just .mq-tag, so this reliably beats the shared
     .tag{color:...} rule regardless of source order. */
  .tag.mq-tag{color:#000;}
  .client-grid{display:grid; grid-template-columns:repeat(5,1fr);
               gap:clamp(4px,1vw,14px);}
  .client-grid a{display:flex; align-items:center; justify-content:center; text-align:center;
                 min-height:64px; padding:16px clamp(8px,1vw,16px); border-radius:var(--r-sm);
                 transition:opacity .3s var(--ease), background .3s var(--ease);}
  .client-grid a:hover{opacity:.72; background:rgba(255,255,255,.62);}
  .client-grid a:focus-visible{outline:2px solid var(--brass); outline-offset:3px;}
  /* Logos vary a lot in native shape (wordmarks vs. stacked marks vs. a
     boxed lockup), so a fixed width would either crop or leave huge gutters.
     Holding every logo to the same height instead - object-fit:contain
     inside that box - is what makes the row read as "the same size": each
     mark occupies an equal visual footprint even though a wide wordmark and
     a squarish stacked mark end up different widths. */
  .client-grid img{height:clamp(26px,2.6vw,38px); width:auto; max-width:100%;
                   object-fit:contain; display:block;}
  /* Hall Group's mark is a near-square stack (icon over two lines of type),
     so at the shared height it lands much narrower - and so visually
     smaller - than the wordmarks next to it. Sized up on its own to read as
     the same weight on the page. */
  .client-grid a[aria-label="Hall Group"] img{height:clamp(40px,4vw,58px);}
  @media (max-width:820px){
    .client-grid{grid-template-columns:repeat(2,1fr);}
    .client-grid a[aria-label="Hall Group"] img{height:clamp(36px,9vw,48px);}
    .client-grid a{min-height:56px;}
    .client-grid img{height:clamp(24px,6vw,32px);}
  }

  /* ============ CONTACT ============ */
  .contact{position:relative; background:transparent;}
  .contact-bg{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; z-index:0;
              filter:grayscale(.55) hue-rotate(-8deg) saturate(.95) contrast(1.04) brightness(1.04);}
  .contact-scrim{position:absolute; inset:0; z-index:1; pointer-events:none;
                 background:linear-gradient(180deg, rgba(245,248,250,.80) 0%, rgba(245,248,250,.46) 40%,
                                                    rgba(245,248,250,.60) 78%, rgba(245,248,250,.92) 100%),
                            radial-gradient(80% 70% at 30% 45%, rgba(245,248,250,.74) 0%, transparent 72%);}
  .contact > .wrap{position:relative; z-index:2;}
  .contact h2{font-size:clamp(30px,7.4vw,124px); font-weight:600; letter-spacing:4px; line-height:1;}
  .c-grid{display:grid; grid-template-columns:repeat(12,1fr); gap:26px; margin-top:clamp(40px,7vh,76px);}
  .c-info{grid-column:1 / span 4;}
  .c-info .row{padding:14px 0;}
  .c-info .row b{display:block; font-size:9.5px; letter-spacing:3px; text-transform:uppercase; color:var(--green); font-weight:500; margin-bottom:4px;}
  .c-info .row a{transition:color .3s, border-color .3s; border-bottom:1px solid rgba(30,58,92,.22); padding-bottom:1px;
                 display:inline-block;}
  .c-info .row p + p{margin-top:2px;}
  .c-info .row a:hover{color:var(--color-charcoal); border-color:var(--brass);}
  .c-form{grid-column:6 / span 7; display:flex; flex-direction:column;}
  .c-form input,.c-form textarea{background:var(--glass-2); border:1px solid var(--glass-edge);
        -webkit-backdrop-filter:var(--glass-blur); backdrop-filter:var(--glass-blur);
        border-radius:var(--r-md); margin-bottom:14px; box-shadow:var(--sh-sm), var(--glass-hi);
        padding:18px 18px; font-family:var(--f-text); font-weight:300; font-size:16px; color:var(--ink); outline:none;
        transition:border-color .3s, box-shadow .3s, background .3s;}
  .c-form input::placeholder,.c-form textarea::placeholder{color:var(--muted-l); text-transform:uppercase; letter-spacing:2.5px; font-size:11px;}
  .c-form input:focus,.c-form textarea:focus{border-color:rgba(30,58,92,.35); background:var(--glass-3);
        box-shadow:var(--sh-md), var(--glass-hi), 0 0 0 4px rgba(30,58,92,.07);}
  .c-form textarea{resize:none; margin-bottom:32px;}
  /* visually hidden but still read aloud - the placeholders are decorative,
     the labels are what a screen reader announces */
  .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;}
  .hp{position:absolute; left:-9999px; opacity:0; height:0; overflow:hidden;}
  .c-form input:invalid:not(:placeholder-shown),
  .c-form textarea:invalid:not(:placeholder-shown){border-color:rgba(122,46,60,.55);}
  .f-status{margin-top:20px; font-size:14px; line-height:1.6; min-height:1px;}
  .f-status.ok{color:var(--color-charcoal);}
  .f-status.err{color:var(--brass);}
  .f-status a{border-bottom:1px solid rgba(122,46,60,.4);}
  .send[disabled]{opacity:.6; cursor:default;}
  /* tinted glass - Apple allows the material to carry a colour */
  .send{align-self:flex-start; color:#fff; padding:20px 54px; font-family:var(--f-ui); font-weight:500;
        font-size:10.5px; letter-spacing:.3em; margin-right:-.3em; text-transform:uppercase; border-radius:999px;
        background:linear-gradient(150deg, rgba(30,58,92,.94) 0%, rgba(21,43,68,.86) 48%, rgba(42,78,119,.92) 100%);}
  .send{box-shadow:
      0 2px 6px rgba(21,43,68,.14),
      0 10px 26px rgba(21,43,68,.18),
      inset 0 1px 0 rgba(255,255,255,.20),
      inset 0 -10px 18px -12px rgba(0,0,0,.24);}
  .send:hover{--lq-bright:1.10;}

  /* ============ FOOTER ============ */
  /* A single slim band: lockup on the left, page links on the right, with the
     copyright on a hairline underneath. The old footer stacked an oversized
     stepped nav, a contact/social column and a separate lockup row, which ran
     several hundred pixels tall on every page. */
  footer{position:relative; color:var(--color-charcoal);
          padding:clamp(20px,2.8vh,32px) 0 clamp(14px,2vh,22px);
          background:rgba(255,255,255,.55);
          border-top:1px solid var(--color-border);}
  .f-row{display:flex; align-items:center; justify-content:space-between;
         gap:clamp(18px,3vw,48px); flex-wrap:wrap;}
  .f-brand{display:flex; align-items:center;}
  .f-nav{display:flex; align-items:center; flex-wrap:wrap;
         gap:clamp(14px,2.2vw,34px);}
  .f-nav a{font-family:var(--f-ui); font-weight:400; text-transform:uppercase;
           font-size:12px; letter-spacing:.16em; margin-right:-.16em;
           color:var(--color-charcoal); transition:color .3s;}
  .f-nav a:hover{color:var(--brass);}
  .f-end{display:flex; justify-content:space-between; flex-wrap:wrap; gap:12px;
         margin-top:clamp(14px,2vh,20px); padding-top:clamp(12px,1.6vh,18px);
         border-top:1px solid var(--color-border);}
  .f-end p{font-size:10px; letter-spacing:2px; text-transform:uppercase; color:var(--muted-l);}

  /* ---- ordering corrections (must sit after .rv and the section rules) ---- */

  /* .pr-bio is transform-driven by the scroll handler every frame; a transition
     on transform would smear it a third of a second behind the scroll */
  .pr-bio{transition:none;}

  /* .rv.in{transform:none} lands after the liquid hover rule at equal
     specificity, so the stat lift needs to out-specify it */
  .stat.rv.in:hover{transform:translateY(-2px);}

  /* inputs cannot carry ::before/::after, so they get the flat glass recipe */
  .c-form input,.c-form textarea{-webkit-backdrop-filter:blur(14px) saturate(190%) brightness(1.05);
        backdrop-filter:blur(14px) saturate(190%) brightness(1.05);
        background:linear-gradient(150deg, rgba(255,255,255,.62), rgba(255,255,255,.34));
        border:1px solid rgba(255,255,255,.75);
        box-shadow:0 2px 10px rgba(21,43,68,.05), inset 0 1px 0 rgba(255,255,255,.95),
                   inset 0 -10px 18px -14px rgba(30,58,92,.30);}
  .c-form input:focus,.c-form textarea:focus{
        background:linear-gradient(150deg, rgba(255,255,255,.80), rgba(255,255,255,.52));
        border-color:rgba(255,255,255,.95);
        box-shadow:0 6px 20px rgba(21,43,68,.08), inset 0 1px 0 rgba(255,255,255,1),
                   0 0 0 4px rgba(122,46,60,.13);}

  @media(max-width:1024px){
    .svc-layout{grid-template-columns:1fr; gap:28px;}
    .svc-media{position:relative; top:0; height:46vh; order:-1;}
    .idx-row h3{opacity:1; transform:none !important;}
    .idx-wrap,.svc-media{transform:none !important; opacity:1 !important;}
    .idx-row .n{opacity:1;}
    .idx-row{padding:18px 0;}
    .svc-panel{grid-template-columns:1fr; gap:24px;}
    .svc-shot{height:40vh;}
    /* Hero zoom runs on mobile too, so the section keeps the JS-set height and
       the stage stays pinned. (height is set inline by sizeHERO - no !important
       here, or the inline value would be ignored and there'd be no runway.) */
    .hero-stick{position:sticky; top:0; height:100svh;}
    /* white-space stays `normal` for the no-JS case, where nothing measures the
       mark and a single line would overflow. With JS, fitHero sets nowrap
       inline so the zoom can't reflow the text mid-scroll. */
    .hero-mark h1{white-space:normal; letter-spacing:0.08em; margin-right:-0.08em;}
    .stats-grid{grid-template-columns:1fr;}
    .lead-grid p,.c-info,.c-form{grid-column:1 / -1;}
    /* Side-by-side only works with room to spare - stack the title, then the
       photo, then the facts below that width. One column now, so the areas
       are redeclared to match (an area string has to carry the same column
       count as grid-template-columns). */
    .feature-grid{grid-template-columns:1fr;
                  grid-template-areas:"head" "media" "info";
                  gap:24px;}
    .feature-info{padding-right:clamp(20px,4vw,64px);}
    .specs{margin-top:0;}
    .specs div:first-child{border-top:none; padding-top:0;}
    /* The projects rail is scroll-driven on mobile now too. It is gated on .js
       because the whole effect is JS-positioned: without the script the track
       never translates, so a pinned 100svh stage would strand every card after
       the first. No-JS therefore keeps the old swipe row, which stays reachable.
       The two cannot both be on - a native scroll container and a translateX
       would each try to own the horizontal position. */
    .hs-inner{position:relative; height:auto; padding:70px 0 40px;}
    .hs-track{overflow-x:auto; scroll-snap-type:x mandatory; padding-bottom:18px;}
    .hs-card{scroll-snap-align:start;}
    .hs-head{position:relative; top:auto; margin-bottom:28px;}
    .js .hs-inner{position:sticky; top:0; height:100svh; overflow:hidden;}
    .js .hs-track{overflow-x:visible; scroll-snap-type:none;}
    .js .hs-card{scroll-snap-align:none;}
    /* clears the 89px sticky site header - at 8vh the progress bar sat behind it */
    .js .hs-head{position:absolute; top:clamp(106px,14vh,132px); margin-bottom:0;}
    .hs-card[style*="--ar"]{flex:0 0 min(78vw, calc(var(--card-h) * var(--ar)));}
    /* principals stack on mobile */
    .principals{height:auto !important;}
    .pr-sticky{position:relative; height:auto; display:block; padding:70px 0;}
    .pr-sticky > .pr-label, .pr-sticky > .pr-card, .pr-sticky > .pr-prog{position:relative;}
    .pr-label{position:relative; top:auto; margin-bottom:40px; opacity:1 !important; transform:none !important;}
    /* Side padding matches .wrap, so the bio cards sit on the same 20px gutter
       as .stat and .svc-panel. At padding:0 they ran edge to edge and the 28px
       corner radius had nothing to sit against. */
    .pr-card{position:relative; inset:auto; opacity:1 !important;
             transform:none !important; margin-bottom:60px;
             padding:0 clamp(20px,4vw,64px);}
    .pr-photo{width:118px; height:118px; transform:none !important; opacity:1 !important;}
    .pr-body{grid-template-columns:1fr; gap:22px;}
    .pr-name,.pr-body,.pr-photo{transform:none !important;}
    .pr-bio{transform:none !important; opacity:1 !important;}
    .pr-prog{display:none;}
  }

  /* ============ TOUCH / SMALL-SCREEN CORRECTIONS ============
     Placed last so it wins over the desktop rules above.               */
  @media (max-width:640px), (pointer:coarse){
    /* 9.5px micro-caps are fine on a monitor at arm's length and far too
       small on a phone; tracking comes down with the size so the words
       still hold together */
    .tag,.stat span,.hs-card .info span,.specs div b,.pr-bio .facts b,
    .c-info .row b,.feature-info .loc,.pj-facts div b,
    .pj-impact h4,.pj-body .cat,.pj-body .loc,.svc-cap span,.svc-txt .n,
    .hero-scroll,.f-end p,.tagline-sec .cta,.send{font-size:11px; letter-spacing:.2em;}
    .hero-mark .sub{font-size:10.5px; letter-spacing:.24em; margin-right:-.24em;}
    .pr-bio .role{font-size:11px; letter-spacing:.24em;}

    /* every interactive target clears the 44px minimum */
    .brand{min-width:48px; min-height:48px; align-items:center; justify-content:center;}
    .brand-sym{height:42px;}
    .burger{padding:15px 17px;}
    .c-info .row a{display:flex; align-items:center; min-height:44px;}
    /* The principals' phone numbers moved from .c-info into .talk-info when
       the home page contact section was rebuilt, and were left behind by the
       rule above - they were 17px tall, well under the 24px minimum, on the
       most important tap target the page has. The hit area is grown with a
       pseudo-element rather than min-height so the underline stays tight to
       the number instead of dropping 44px below it. */
    .talk-info .row a{position:relative;}
    .talk-info .row a::after{content:''; position:absolute; left:0; right:0;
                             top:50%; height:44px; transform:translateY(-50%);}
    .f-nav a{display:flex; align-items:center; min-height:44px;}
    .menu-nav a{padding:14px 0;}
    .pj-dots button{width:14px; height:14px;}
    .pj-close{width:48px; height:48px;}
    .tagline-sec .cta{padding-top:12px; padding-bottom:16px;}
    .hs-card .info{padding-top:16px; padding-bottom:8px;}

    /* long email addresses must never widen the page */
    .c-info .row a{word-break:break-word; overflow-wrap:anywhere;}
    .c-form input,.c-form textarea{font-size:16px;}
  }

  /* NOTE: horizontal overflow is contained by body{overflow-x:hidden} only.
     Do NOT add overflow to <html> - it makes the root a scroll container and
     position:sticky then resolves against it, which silently stops every
     pinned section (hero zoom, principals, projects rail, services) working. */

  /* five labelled segments cannot fit across a phone, so the divided bar
     becomes a stack of full-width rows - same interaction, readable labels */
  @media (max-width:760px){
    .svc-bar{grid-template-columns:1fr; gap:0;}
    .svc-seg{flex-direction:row; align-items:center; justify-content:flex-start;
             gap:12px; flex-wrap:wrap; text-align:left;
             padding:14px 0 12px; min-height:52px;
             border-bottom:1px solid rgba(30,58,92,.12);}
    .svc-seg i{flex:1 0 100%; margin-top:0; height:2px;}
    .svc-seg.is-on i{transform:scaleY(2);}
    .svc-seg-name{font-size:16px;}
  }

  /* ============ "MORE INFO" AFFORDANCE ============
     A glass plus at the foot of every project image. The card itself is
     already the button (role=button, tabindex, keyboard handler), so this is
     decorative and hidden from screen readers - nesting a real button inside
     a button would be invalid and would double up in the tab order.

     Centred with a negative margin rather than translateX(-50%), because the
     liquid-glass recipe animates `transform` on press and the two would fight. */
  .pj-plus{--sz:clamp(38px,3.1vw,52px);
           position:absolute; z-index:3; pointer-events:none;
           width:var(--sz); height:var(--sz); left:50%; margin-left:calc(var(--sz) / -2);
           bottom:clamp(12px,1.5vw,20px); border-radius:50%;
           display:grid; place-items:center;
           font-family:var(--f-ui); font-weight:400; line-height:1;
           font-size:calc(var(--sz) * .52); color:var(--color-charcoal);
           opacity:.92;}
  /* it reacts to the whole card, not just itself - the card is the target */
  .hs-card:hover .pj-plus, .feature:hover .pj-plus,
  .hs-card:focus-visible .pj-plus, .feature:focus-visible .pj-plus{
      opacity:1; transform:scale(1.12); --lq-bright:1.16;}
  .hs-card:hover .pj-plus::after, .feature:hover .pj-plus::after{opacity:1;}
  @media (max-width:760px){ .pj-plus{--sz:40px;} }

  /* If the video CDN is unreachable the element must stay invisible scenery -
     without this the browser paints its native player controls across the page. */
  video{pointer-events:none;}
  video::-webkit-media-controls,
  video::-webkit-media-controls-enclosure,
  video::-webkit-media-controls-panel{display:none !important; -webkit-appearance:none;}

  /* ==========================================================================
     MULTI-PAGE COMPONENTS
     Added when the site went from one long page to Home / About / Projects /
     Services. Everything below reuses the existing tokens - no new colours, no
     new type families - so the sub-pages read as the same site.
     ========================================================================== */

  /* current page in the menu */
  .menu-nav a.here{color:var(--color-brass);}

  /* ---- page header: the compact hero every sub-page opens with ---- */
  .page-hero{position:relative; background:var(--color-linen);
             padding:clamp(150px,20vh,230px) 0 clamp(56px,8vh,96px);
             border-bottom:1px solid var(--color-border);}
  .page-hero h1{font-family:var(--f-display); font-weight:600;
                font-size:clamp(38px,6.4vw,86px); letter-spacing:var(--ls-head);
                line-height:1.04; color:var(--color-charcoal); margin:18px 0 0;}
  .page-hero .lede{margin-top:clamp(16px,2.4vh,26px); max-width:58ch;}

  /* ---- ABOUT: photo-slideshow hero ----
     The four-frame slideshow that used to run behind the home page hero now
     backs this one. The slideshow's own parts (.hero-slides, .hs-slide,
     .hs-bar, .hs-loc) are reused untouched - same crossfade, same scrubber,
     same caption - so only the container and the type on top need stating
     here. The photos are full-bleed daylight city shots, so the copy goes
     white over a dark grade, the same treatment the home hero gives its
     footage. A separate veil class from .hero-veil on purpose: that one is
     overridden with !important further down for the intro film. */
  .page-hero-photo{background:var(--color-midnight); overflow:hidden;
                   border-bottom:0; min-height:min(74svh,720px);
                   display:grid; align-content:center;}
  .page-hero-photo .hero-slides{z-index:0;}
  .ph-veil{position:absolute; inset:0; z-index:1; pointer-events:none;
           background:linear-gradient(180deg, rgba(8,16,28,.62) 0%,
                                              rgba(8,16,28,.44) 34%,
                                              rgba(8,16,28,.46) 70%,
                                              rgba(8,16,28,.66) 100%);}
  .page-hero-photo > .wrap{position:relative; z-index:2;}
  .page-hero-photo h1{color:#fff; text-shadow:0 2px 24px rgba(8,16,28,.6);}
  .page-hero-photo .lede{color:rgba(255,255,255,.9);
                         text-shadow:0 1px 14px rgba(8,16,28,.6);}
  .page-hero-photo .tag{color:#fff; opacity:.82;}
  /* the scrubber sits at the very top of the section, which runs up under the
     fixed header - nudge it clear so it isn't hidden behind the nav */
  .page-hero-photo .hs-bar{top:clamp(84px,11vh,104px);}
  .page-hero-photo .hs-loc{top:calc(clamp(84px,11vh,104px) + 28px);}
  /* On a phone the hero is much taller relative to its width, so the copy
     sits lower - right over the brightest part of the Bethesda Terrace frame,
     where the worst-case pixel behind the lede measured only 3.06:1 against
     white (4.5:1 is the minimum for body text). The other three frames were
     comfortably clear, so rather than darken the whole treatment this just
     deepens the lower half of the veil at this breakpoint. */
  @media (max-width:900px){
    .ph-veil{background:linear-gradient(180deg, rgba(8,16,28,.62) 0%,
                                                rgba(8,16,28,.50) 30%,
                                                rgba(8,16,28,.62) 62%,
                                                rgba(8,16,28,.74) 100%);}
  }

  /* ---- PROJECTS: navy band headline (no hero imagery) ----
     A block of navy running off the left edge of the page and stopping short
     of the right, with the headline inside it. It is deliberately NOT inside
     .wrap: the band has to bleed past the page gutter on the left, so it
     carries that gutter as its own padding instead, which keeps the type on
     the same left margin as everything below it. */
  .pg-hero{background:#fff; padding:clamp(104px,13vh,150px) 0 0;}
  /* Rock texture under a navy tint. The tint is a gradient layer stacked over
     the photo in the same background shorthand, so it needs no extra element.
     Set at .62/.72 rather than heavier: measured against the brightest pixel
     in the band, white type still lands at 8:1 - far clear of the 3:1 large-
     text line - so the stone can stay legible as stone instead of being
     flattened into a plain navy panel. */
  .pg-hero-band{background:
                  linear-gradient(180deg, rgba(30,58,92,.62), rgba(21,43,68,.72)),
                  url('band-rock.jpg') center/cover no-repeat,
                  var(--color-navy);
                color:#fff;
                width:min(78%,1180px); border-radius:0;
                padding:clamp(34px,5vw,72px) clamp(30px,4.5vw,80px)
                        clamp(34px,5vw,72px) clamp(20px,4vw,64px);}
  .pg-hero-band h1{font-family:var(--f-ui); font-weight:300; color:#fff;
                   text-transform:none; letter-spacing:.004em;
                   font-size:clamp(34px,6.6vw,104px); line-height:.94;
                   margin:0; text-shadow:none;}
  .pg-hero-band h1 em{font-style:normal; font-family:inherit; font-weight:500;
                      color:inherit; letter-spacing:inherit;}
  @media (max-width:900px){
    /* at narrow widths a band that stops short reads as a mistake rather than
       a device - let it run the full width instead */
    .pg-hero-band{width:100%;}
  }
  /* the rule and supporting line sit between the band and the cards */
  .pg-hero + .pg-sec{padding-top:clamp(40px,6vh,72px);}
  /* hairline divider under the headline, spanning the full column */
  .ph-rule{height:0; border:0; border-top:1px solid var(--color-border);
           margin:0 0 clamp(18px,2.8vh,30px);}
  /* centred on the page beneath the rule, rather than sharing the headline's
     left margin */
  .ph-lede{margin:0 auto clamp(30px,5vh,56px); max-width:52ch;
           text-align:center;}
  /* Combined selector so this beats the shared .lede{color:var(--muted-l)}
     rule, which sits later in the file and would otherwise win at equal
     specificity. */
  .lede.ph-lede{color:#000;}
  .lede{font-size:clamp(15px,1.5vw,19px); line-height:1.62; color:var(--muted-l);}

  /* ---- primary button ---- */
  .btn-primary{display:inline-flex; align-items:center; gap:10px;
               margin-top:clamp(24px,3.4vh,38px); padding:15px 30px;
               border-radius:999px; background:var(--color-brass); color:#fff;
               font-family:var(--f-ui); font-size:12px; letter-spacing:var(--ls-label);
               margin-right:calc(var(--ls-label) * -1); text-transform:uppercase;
               box-shadow:var(--sh-md); transition:transform .4s var(--spring),
               box-shadow .4s var(--ease), background .3s;}
  .btn-primary:hover{transform:translateY(-2px); box-shadow:var(--sh-lg);
                     background:var(--color-navy);}

  /* ---- home: short services teaser that points at the Services page ---- */
  .svc-teaser{background:var(--color-salt); padding:clamp(70px,11vh,130px) 0; text-align:center;}
  .svc-teaser h2{font-family:var(--f-display); font-weight:600; margin:16px 0 0;
                 font-size:clamp(28px,4vw,54px); letter-spacing:var(--ls-head);
                 color:var(--color-charcoal);}
  .svc-teaser .lede{margin:18px auto 0; max-width:62ch;}
  .teaser-list{display:flex; flex-wrap:wrap; justify-content:center;
               gap:10px clamp(12px,1.6vw,20px); margin-top:clamp(26px,4vh,40px);}
  .teaser-list span{font-family:var(--f-ui); font-size:11.5px; text-transform:uppercase;
                    letter-spacing:var(--ls-label); margin-right:calc(var(--ls-label) * -1);
                    color:var(--color-navy); padding:10px 18px; border-radius:999px;
                    border:1px solid var(--color-border); background:rgba(255,255,255,.6);}

  /* ---- closing call to action, shared by the sub-pages ---- */
  /* Heading and button share one line, side by side, rather than the button
     sitting stacked underneath a centred heading. */
  .cta-strip{position:relative; z-index:2;
             padding:clamp(56px,9vh,110px) 0 clamp(70px,11vh,130px);}
  /* centred as a pair rather than pushed to opposite edges, so the heading
     and its button read as one unit instead of two far-apart elements */
  .cta-strip .wrap{display:flex; align-items:center; justify-content:center;
                   gap:clamp(18px,2.4vw,34px); flex-wrap:wrap;}
  .cta-strip h2{font-family:var(--f-display); font-weight:600; margin:0;
                font-size:clamp(24px,3.2vw,44px); letter-spacing:var(--ls-head);
                color:var(--color-charcoal);}
  /* .btn-primary carries a top margin for its stacked use elsewhere; on one
     line that would knock it out of vertical alignment with the heading. */
  .cta-strip .btn-primary{margin-top:0; flex:none;}

  /* ---- about: the two-column pitch ---- */
  .about-pitch{background:#fff; padding:clamp(70px,11vh,130px) 0;}
  .ap-grid{display:grid; grid-template-columns:1fr 1fr; gap:clamp(30px,5vw,80px);}
  .ap-col h2{font-family:var(--f-display); font-weight:600; margin:0 0 18px;
             font-size:clamp(22px,2.6vw,36px); letter-spacing:var(--ls-head);
             color:var(--color-charcoal);}
  .ap-col p{font-size:15px; line-height:1.68; color:var(--muted-l); margin-bottom:14px;}
  .ap-stats{display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(14px,2vw,26px);
            margin-top:clamp(44px,7vh,80px);}

  /* ---- about: team cards ---- */
  .team-sec{background:var(--color-linen); padding:clamp(70px,11vh,130px) 0;}
  .team-lede{margin:-6px 0 clamp(30px,5vh,52px);}
  .team-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(16px,2.2vw,30px);}
  .team-card{position:relative; border-radius:var(--r-lg); overflow:hidden;
             cursor:pointer; text-align:left; padding:0;
             transition:transform .5s var(--spring), box-shadow .5s var(--ease);}
  .team-card:hover{transform:translateY(-5px); box-shadow:var(--sh-xl);}
  .team-card:focus-visible{outline:2px solid var(--color-brass); outline-offset:3px;}
  .tc-photo{aspect-ratio:1/1; overflow:hidden; background:var(--color-salt);}
  .tc-photo img{width:100%; height:100%; object-fit:cover; display:block;
                transition:transform 1.2s cubic-bezier(.16,.9,.3,1);}
  .team-card:hover .tc-photo img{transform:scale(1.04);}
  .tc-meta{padding:clamp(20px,2.4vw,30px);}
  .tc-meta .role{display:block; font-family:var(--f-ui); font-size:9.5px;
                 letter-spacing:var(--ls-label); margin-right:calc(var(--ls-label) * -1);
                 text-transform:uppercase; color:var(--color-brass); margin-bottom:8px;}
  .tc-meta h3{font-family:var(--f-display); font-weight:600; margin:0;
              font-size:clamp(19px,2vw,27px); letter-spacing:var(--ls-head);
              color:var(--color-charcoal);}
  .tc-lead{font-size:13.5px; line-height:1.6; color:var(--muted-l); margin:12px 0 0;}
  .tc-more{display:inline-block; margin-top:16px; font-family:var(--f-ui); font-size:10.5px;
           letter-spacing:var(--ls-label); margin-right:calc(var(--ls-label) * -1);
           text-transform:uppercase; color:var(--color-navy);}
  .tc-more i{font-style:normal; transition:transform .4s var(--spring); display:inline-block;}
  .team-card:hover .tc-more i{transform:translateX(4px);}

  /* The biography sits in the markup so it survives without JS. Only hide it
     once scripting has confirmed it can put the text in the modal instead. */
  .team-bio{padding:0 clamp(20px,2.4vw,30px) clamp(22px,2.6vw,30px);}
  .team-bio p{font-size:14px; line-height:1.7; color:var(--muted-l); margin-bottom:12px;}
  .team-bio .facts{display:grid; gap:11px; margin-top:16px;}
  .team-bio .facts div{font-size:12.5px; color:var(--muted-l); line-height:1.5;}
  .team-bio .facts b{display:block; color:var(--color-brass); font-weight:500;
                     font-size:9.5px; letter-spacing:2.5px; text-transform:uppercase;
                     margin-bottom:3px;}
  .js .team-bio{display:none;}
  .js .team-card{cursor:pointer;}

  /* ---- bio modal (reuses the project lightbox shell) ---- */
  .bio-modal .pj-panel{max-width:min(760px,92vw);}

  /* ---- expanded project card ----
     Same treatment as the expanded bios on About: a plain opaque panel with
     square corners and no liquid glass. The shared .pj-panel rule carries the
     glass (translucent fill, backdrop blur, layered shadow) plus two
     decorative pseudo-layers - a conic edge highlight and a pointer-tracked
     sheen - so all of that has to be switched off here, not just the
     background. Scoped to #pjModal, which is the project lightbox on the home
     and projects pages; the bio modal keeps its own overrides. */
  #pjModal .pj-panel{background:#fff; border-radius:0; box-shadow:none;
                     -webkit-backdrop-filter:none; backdrop-filter:none;}
  #pjModal .pj-panel::before,
  #pjModal .pj-panel::after{display:none;}
  /* No maroon inside this panel - the category label, the fact labels and the
     impact heading all pick up the shared oxblood accent, which is kept
     everywhere else on the site but not here. Navy for the small caps labels,
     black for the body copy they sit above. */
  #pjModal .pj-body .cat,
  #pjModal .pj-facts div b,
  #pjModal .pj-impact h4{color:var(--color-navy);}
  #pjModal .pj-body h3{color:#000;}
  #pjModal .pj-dots button:hover{border-color:var(--color-navy);}
  .bio-body{padding:clamp(26px,3.4vw,48px);}
  .bio-head{display:flex; align-items:center; gap:clamp(16px,2.2vw,28px);
            margin-bottom:clamp(20px,2.6vh,30px);}
  .bio-photo{width:clamp(78px,9vw,116px); height:clamp(78px,9vw,116px); flex:none;
             border-radius:50%; overflow:hidden; box-shadow:var(--sh-md);}
  .bio-photo img{width:100%; height:100%; object-fit:cover; display:block;}
  .bio-body .facts{display:grid; gap:12px; margin-top:20px;
                   padding-top:20px; border-top:1px solid var(--color-border);}
  .bio-body .facts div{font-size:13px; color:var(--muted-l); line-height:1.5;}
  .bio-body .facts b{display:block; color:var(--color-brass); font-weight:500;
                     font-size:9.5px; letter-spacing:2.5px; text-transform:uppercase;
                     margin-bottom:3px;}

  /* ---- projects page: the rail cards, laid out as a grid ---- */
  .pg-sec{background:#fff; padding:clamp(60px,9vh,110px) 0 clamp(40px,6vh,70px);}
  .pg-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(16px,2.2vw,30px);}
  /* the rail sizes cards by flex-basis; in a grid the track owns the width */
  .pg-card{flex:none !important; width:auto;}
  /* Square corners here, matching the navy band above. Scoped to .pg-card so
     the shared .hs-card rail on the home page keeps its rounded frames. */
  .pg-card .m{border-radius:0;}
  .pg-card .m{aspect-ratio:3/2; overflow:hidden;}
  .pg-card .m img{width:100%; height:100%; object-fit:cover;}
  .pg-card .info{padding-top:18px;}
  /* Plus badge for the projects-page cards - a separate class from the
     shared .pj-plus (which stays a centered, always-on glass badge for the
     home page rail) so this can match the About page's .tc-plus treatment
     instead: a navy-and-white circle, bottom-left, hidden until the card is
     hovered or keyboard-focused. */
  .pg-plus{position:absolute; z-index:3; pointer-events:none;
           left:14px; bottom:14px; width:36px; height:36px;
           border-radius:50%; background:var(--color-navy); color:#fff;
           display:flex; align-items:center; justify-content:center;
           font-family:var(--f-ui); font-weight:400; font-size:20px; line-height:1;
           opacity:0; transform:translateY(6px) scale(.9);
           transition:opacity .3s var(--ease), transform .3s var(--ease);}
  .pg-card:hover .pg-plus, .pg-card:focus-visible .pg-plus{
    opacity:1; transform:translateY(0) scale(1);}

  @media (max-width:1024px){
    .ap-grid{grid-template-columns:1fr; gap:34px;}
    .team-grid{grid-template-columns:repeat(2,1fr);}
    .pg-grid{grid-template-columns:repeat(2,1fr);}
    .ap-stats{grid-template-columns:1fr;}
  }
  @media (max-width:680px){
    .team-grid,.pg-grid{grid-template-columns:1fr;}
    .page-hero{padding-top:clamp(126px,17vh,170px);}
    .bio-head{flex-direction:column; align-items:flex-start; gap:14px;}
  }

  /* attribution line under a project's impact copy */
  .pj-attrib{margin-top:16px; padding-top:14px; border-top:1px solid var(--color-border);
             font-size:12.5px; line-height:1.6; color:var(--muted-l);}
  .pj-attrib strong{color:var(--color-navy); font-weight:600;}

  /* ==========================================================================
     LEGIBILITY PASS + HEADER NAV + WHAT WE DO
     A large share of this audience is older, so the small type across the site
     was raised. These rules come last on purpose: they are deliberate overrides
     of the sizes set earlier in the file, kept together so the decision is easy
     to find and easy to reverse.
     ========================================================================== */

  /* ---- hero tagline: was clamp(9px,1vw,11.5px), which is very small for a
          line that has to be read at a glance ---- */
  .hero-mark .sub{font-size:clamp(13px,1.45vw,19px); letter-spacing:.30em;
                  margin-right:-.30em; font-weight:500; margin-top:30px;}

  /* ---- scroll cue: a readable word plus a bar that travels down the rail ---- */
  .hero-scroll{bottom:30px; display:flex; flex-direction:column; align-items:center;
               gap:12px; opacity:1; font-size:0; letter-spacing:normal; margin-right:0;}
  .hero-scroll .hs-word{font-family:var(--f-ui); font-size:14.5px; font-weight:500;
                        letter-spacing:.30em; margin-right:-.30em; text-transform:uppercase;
                        color:var(--color-charcoal);}
  .hero-scroll .hs-rail{position:relative; display:block; width:3px; height:58px;
                        border-radius:3px; background:rgba(30,58,92,.22); overflow:hidden;}
  .hero-scroll .hs-rail i{position:absolute; left:0; width:100%; height:24px; border-radius:3px;
                          background:var(--color-brass); animation:hsDrop 1.9s cubic-bezier(.6,0,.4,1) infinite;}
  @keyframes hsDrop{
    0%   {transform:translateY(-24px); opacity:0;}
    22%  {opacity:1;}
    78%  {opacity:1;}
    100% {transform:translateY(58px);  opacity:0;}
  }
  @media (prefers-reduced-motion:reduce){
    .hero-scroll .hs-rail i{animation:none; transform:translateY(16px);}
  }

  /* ---- stat captions: these sit under the big numerals ---- */
  .stat span{font-size:clamp(13px,1.3vw,16px) !important; letter-spacing:.24em !important;
             margin-right:-.24em !important; line-height:1.5; color:rgba(255,255,255,.72);}

  /* ---- HEADER: links laid out horizontally, brand on the left ---- */
  #hdr nav{display:flex; align-items:center; gap:clamp(16px,2.4vw,40px);}
  .brand{display:flex; align-items:center; gap:12px; margin-right:auto;}
  .brand-words{display:none; font-family:var(--f-ui); text-transform:uppercase;
               line-height:1.14; color:var(--color-charcoal);}
  .brand-words .l1{display:block; font-size:15px; font-weight:500; letter-spacing:.2em; margin-right:-.2em;}
  .brand-words .l2{display:block; font-size:11px; font-weight:400; letter-spacing:.2em;
                   margin-right:-.2em; color:var(--color-text-secondary);}
  .nav-links{display:none; align-items:center; gap:clamp(14px,2vw,34px);}
  .nav-links a{font-family:var(--f-text); font-size:15.5px; font-weight:500;
               color:var(--color-charcoal); white-space:nowrap; position:relative;
               padding:6px 0; transition:color .3s;}
  /* Navy and white at rest, maroon only ever under the cursor. The header
     used to use maroon as a standing accent - the current page's link, its
     rule, and the filled CTA - but that reads as colour sitting there for no
     reason: the visitor already knows which page they are on. Keeping maroon
     for hover alone makes it mean something. */
  .nav-links a::after{content:''; position:absolute; left:0; right:0; bottom:0; height:1.5px;
                      background:var(--color-navy); transform:scaleX(0); transform-origin:50%;
                      transition:transform .35s var(--ease), background .3s;}
  .nav-links a:hover{color:var(--brass);}
  .nav-links a:hover::after{background:var(--brass);}
  .nav-links a:hover::after,.nav-links a.here::after{transform:scaleX(1);}
  .nav-links a.here{color:var(--color-navy);}
  /* .here and :hover carry identical specificity, and .here is declared last,
     so without this the current page's own link is the one link that stays
     navy under the cursor. */
  .nav-links a.here:hover{color:var(--brass);}
  .nav-cta{display:none; align-items:center; gap:9px; padding:12px 24px; border-radius:999px;
           background:var(--color-navy); color:#fff; font-family:var(--f-text);
           font-size:14.5px; font-weight:600; white-space:nowrap; box-shadow:var(--sh-sm);
           transition:transform .4s var(--spring), box-shadow .4s var(--ease), background .3s;}
  .nav-cta:hover{transform:translateY(-2px); box-shadow:var(--sh-md); background:var(--brass);}
  .nav-cta i{font-style:normal; transition:transform .35s var(--spring); display:inline-block;}
  .nav-cta:hover i{transform:translateX(3px);}

  @media (min-width:900px){
    .brand-words{display:block;}
    .nav-links{display:flex;}
    .nav-cta{display:inline-flex;}
    .burger{display:none !important;}      /* links are visible, so the menu is redundant */
  }

  /* ---- WHAT WE DO ----
     Two columns: a magazine-index run of services (+ two accent photos) on
     the left, the lead statement / supporting copy / CTA on the right. */
  /* No vertical padding here on purpose - it lives on .firm-services
     instead (below), so the navy block, stretched to match that column's
     full height, reaches the literal top and bottom edges of the section
     rather than sitting inset inside the section's own padding. */
  .firm-sec{background:#fff;}
  .firm-grid{display:grid; grid-template-columns:1.15fr 1fr; gap:clamp(34px,6vw,96px);
             align-items:stretch;}

  /* -- left: photos and services checkerboarded on a 2-col / 5-row grid -
        photo 1 anchors the top-left corner across the top THREE rows, paired
        with services 1-3 on the right; services 4 and 5 stack on the left
        underneath, with photo 2 anchoring the bottom-right corner across the
        last two. The diagonal still reads; the top photo is simply taller
        than the bottom one now that five services split 3/2 rather than
        2/2. */
  .firm-services{display:grid; grid-template-columns:1fr 1fr;
                 grid-template-rows:repeat(5,auto);
                 grid-template-areas:"p1 s1" "p1 s2" "p1 s3" "s4 p2" "s5 p2";
                 column-gap:clamp(24px,3vw,40px); row-gap:0; align-items:stretch;
                 padding:clamp(48px,7vh,86px) 0;}
  /* The gap between services is what sets this section's height - five of
     them, doubled top and bottom. Trimmed here rather than in the type: the
     headings and copy keep their size and 1.6 line-height, so it reads the
     same, just less airy. */
  .firm-svc{padding:clamp(18px,2.6vh,30px) 0;}
  .firm-svc h3{font-family:var(--f-display); font-weight:600; margin:0 0 8px;
               font-size:clamp(16px,1.5vw,20px); letter-spacing:var(--ls-head);
               color:var(--color-charcoal);}
  .firm-svc p{font-size:14.5px; line-height:1.6; color:var(--color-text-secondary); margin:0;}
  .firm-svc-1{grid-area:s1;}
  .firm-svc-2{grid-area:s2;}
  .firm-svc-3{grid-area:s3;}
  .firm-svc-4{grid-area:s4;}
  .firm-svc-5{grid-area:s5;}

  /* Squared corners - flush blocks rather than floating cards. Oversized and
     pulled up by the same margin on both edges (matches the Star photo's
     treatment) so JS can drift the image inside the frame without ever
     showing a gap at the edge. */
  .firm-photo{display:block; margin:0; position:relative; overflow:hidden;
             border-radius:0; box-shadow:none;}
  .firm-photo img{position:absolute; top:-12%; left:0; width:100%; height:124%;
                  object-fit:cover; display:block;}
  /* No aspect-ratio: each photo simply fills the rows it spans, and the image
     inside crops to whatever height that comes to (object-fit:cover on an
     oversized img, above).

     It used to carry aspect-ratio:3/4, which set a floor on the photo's
     height. The bottom photo spans only two rows, and that floor was taller
     than those two services actually needed - so the grid stretched the rows
     to reach it, and the extra height landed as a wider gap between Project
     Funding and Site Selection than any other pair on the list. Letting the
     copy set the row heights keeps every gap identical. */
  .firm-photo-1,.firm-photo-2{width:100%; min-height:200px;}
  .firm-photo-1{grid-area:p1;}
  .firm-photo-2{grid-area:p2;}

  /* -- right: lead statement, copy, CTA - a plain, static navy block, no
        scroll animation. It's the grid item directly and stretches to match
        the full height of the left column (see align-items:stretch on
        .firm-grid), with its content centered vertically. Every element
        inside reads white, so the two accents that used to carry the brass
        color now lean on italics instead to stand apart. -- */
  .firm-intro{display:flex; flex-direction:column; justify-content:center;
             background:var(--color-navy); color:#fff;
             padding:clamp(40px,5vw,64px); border-radius:0; box-shadow:none;}
  .firm-lead h2{font-family:var(--f-display); font-weight:600; margin:0;
                font-size:clamp(30px,4.4vw,58px); letter-spacing:var(--ls-head);
                line-height:1.08; color:#fff;}
  /* Georgia, matching the serif already used on the featured-project caption -
     the switch of typeface is what sets this clause apart from the sans-serif
     line above it. */
  .firm-lead h2 em{font-style:italic; color:#fff; display:block;
                   font-family:Georgia,'Times New Roman',serif;}
  .firm-copy{margin-top:clamp(22px,3vh,30px);}
  /* the .firm-kicker lead-in line ("Nothing gets built by one side alone.")
     was dropped from the copy, so its rule went with it */
  .firm-copy p{font-size:clamp(15.5px,1.35vw,19px); line-height:1.66;
               color:rgba(255,255,255,.78);}
  .firm-copy strong{color:#fff; font-weight:700;}
  .firm-copy em{font-style:italic; color:#fff; font-weight:500;}
  .btn-ghost{display:inline-flex; align-items:center; gap:10px; margin-top:clamp(24px,3.4vh,36px);
             padding:15px 30px; border-radius:999px; background:#fff;
             color:var(--color-charcoal); font-family:var(--f-text); font-size:15px;
             font-weight:600; box-shadow:none;
             transition:transform .4s var(--spring), color .3s;}
  .btn-ghost:hover{transform:translateY(-2px); color:var(--color-brass);}
  .btn-ghost i{font-style:normal; transition:transform .35s var(--spring); display:inline-block;}
  .btn-ghost:hover i{transform:translateX(4px);}

  @media (max-width:1024px){
    /* Stacked layout - the two columns no longer sit side by side, so the
       "match the left column's height" edge-to-edge treatment stops
       applying. Give the section back its own top/bottom padding and drop
       .firm-services' padding so there's one consistent gap around the
       stack instead of a doubled-up gap between the navy block and the
       photos. */
    .firm-grid{grid-template-columns:1fr; gap:44px;}
    .firm-intro{order:-1;}
    .firm-sec{padding:clamp(70px,11vh,132px) 0;}
    .firm-services{padding:0;}
  }
  @media (max-width:640px){
    /* checkerboard collapses to one column, top to bottom in DOM order */
    .firm-services{grid-template-columns:1fr;
                   grid-template-areas:"p1" "s1" "s2" "s3" "s4" "s5" "p2";}
  }

  /* ---- SITE-WIDE SMALL TYPE ----
     Everything here was between 9px and 13.5px. Raised to a 13px floor, which
     is the smallest size that stays comfortable for older eyes on a phone. */
  .tag{font-size:clamp(11.5px,1.05vw,13.5px) !important; letter-spacing:.26em !important;
       margin-right:-.26em !important;}
  .lede{font-size:clamp(15.5px,1.45vw,19.5px); line-height:1.66;}
  .tc-lead,.ap-col p{font-size:15px; line-height:1.66;}
  .team-bio p,.bio-body p{font-size:15px; line-height:1.72;}
  .team-bio .facts div,.bio-body .facts div,.pr-bio .facts div{font-size:13.5px; line-height:1.58;}
  .team-bio .facts b,.bio-body .facts b,.pr-bio .facts b{font-size:11px; letter-spacing:1.9px;}
  .pr-bio p{font-size:15.5px; line-height:1.7;}
  .hs-card .info h3,.pg-card .info h3{font-size:clamp(17px,1.8vw,23px);}
  .hs-card .info p,.pg-card .info p{font-size:14px; line-height:1.55;}
  .hs-card .info span,.pg-card .info span{font-size:11px !important; letter-spacing:1.9px !important;
                                          margin-right:-1.9px !important;}
  .hs-note p{font-size:15px; line-height:1.66;}
  .pj-facts p{font-size:15px;}
  .pj-facts b{font-size:11px !important; letter-spacing:1.9px !important;}
  .pj-body p,.pj-impact p{font-size:15px; line-height:1.72;}
  .pj-attrib{font-size:14px;}
  .feature-info .loc,.pj-body .loc{font-size:14.5px;}
  .idx-row p,.svc-panel p,.svc-cap{font-size:15px; line-height:1.66;}
  .c-form label,.c-form input,.c-form textarea,.c-form button{font-size:15.5px;}
  .f-nav a{font-size:12.5px;}

  .f-end p{font-size:13px;}
  .teaser-list span{font-size:12.5px;}
  .client-grid a{font-size:clamp(14px,1.35vw,18px);}

  /* Sweep of the 8–9.5px uppercase labels the pass above did not reach. These
     are the eyebrow/category labels; 11px is the floor at which tracked-out
     caps stay comfortable. Found by auditing the smallest rendered text on
     every page rather than by reading the stylesheet. */
  .pr-bio .facts b,.specs div b,.hs-card .info span,.pg-card .info span,
  .pj-body .cat,.pj-facts div b,.pj-impact h4,.c-info .row b,
  .tc-meta .role,.svc-seg-n,.hero-mark .sub{font-size:11.5px !important;}
  .pj-body .cat,.pj-impact h4,.pj-facts div b{letter-spacing:.2em !important;}
  .hero-mark .sub{font-size:clamp(13px,1.45vw,19px) !important;}   /* keep the hero larger still */
  .lockup .words .l2{font-size:clamp(10.5px,1vw,13px);}
  .f-end p{font-size:13.5px;}

  /* ==========================================================================
     HOME REFINEMENTS
     ========================================================================== */

  /* ---- contact backdrop is a photograph now, not a video ---- */
  .talk-photo{position:absolute; inset:0; z-index:0; display:block;}
  /* same grade the video carried, so the photograph sits back behind the form
     rather than competing with it */
  .talk-photo img{width:100%; height:100%; object-fit:cover; display:block;
                  filter:grayscale(.55) hue-rotate(-8deg) saturate(.95) contrast(1.04) brightness(1.04);}

  /* ---- hero wash, eased back a little on request. Re-measured afterwards:
          the wordmark and the tagline both still clear their WCAG thresholds
          against the skyline, with the tagline the tighter of the two. ---- */
  .hero-veil{background:radial-gradient(64% 56% at 50% 47%, rgba(255,255,255,.66) 0%,
                                        rgba(255,255,255,.46) 38%, rgba(255,255,255,.20) 66%,
                                        rgba(255,255,255,.05) 84%, transparent 100%),
                        linear-gradient(180deg, rgba(255,255,255,.22) 0%, rgba(255,255,255,.03) 26%,
                                        rgba(255,255,255,.03) 58%, rgba(245,248,250,.30) 80%,
                                        rgba(245,248,250,.80) 93%, rgba(245,248,250,1) 100%);}

  /* The header is sticky and ~89px tall, so an anchor jump used to park the
     target's own heading underneath it - clicking "Get in touch" hid the
     "Contact us" label. scroll-margin-top reserves that space. */
  section[id], div[id]{scroll-margin-top:clamp(100px,12vh,124px);}

  /* With the scrim eased back so more of the photograph reads through, the
     contact details fell just under AA on a phone (measured 4.26:1 and 3.81:1
     against 4.5). Darkening the type fixes that without putting the wash back -
     the phone numbers in particular have to be readable. */
  .c-info .row a, .c-info .row p{color:var(--color-charcoal); font-weight:500;}
  /* The oxblood eyebrow measured 4.24:1 over the lighter photograph on desktop.
     A deeper shade of the same accent clears 4.5 without losing the brand note. */
  .c-info .row b{color:#63242F;}

  /* Stats sit straight on the photo/video backdrop, not a block, so the
     caption stays white like the numeral above it. */
  .stat span{color:rgba(255,255,255,.72) !important;}

  /* ==========================================================================
     NEW LOCKUP + HOME REFINEMENTS
     ========================================================================== */

  /* the wordmark now ships as one piece of artwork, so the header no longer
     typesets "Winning Development" beside the mark */
  .brand-lockup{height:clamp(30px,3.2vw,42px); width:auto; display:block;}
  /* Header mark ships as a navy PNG - shown at its native color. */
  .foot-lockup{height:clamp(26px,2.6vw,38px); width:auto; display:block;}
  .lockup{display:block;}

  /* Contact wash pulled right back - just enough to hold the type, so the
     photograph reads almost fully. Verified against every text element in the
     section; the copy carries the contrast now, not the scrim. */
  .contact-scrim{background:linear-gradient(180deg, rgba(245,248,250,.775) 0%, rgba(245,248,250,.435) 40%,
                                                    rgba(245,248,250,.565) 78%, rgba(245,248,250,.895) 100%),
                            radial-gradient(80% 70% at 30% 45%, rgba(245,248,250,.705) 0%, transparent 72%);}
  /* The "Contact us" eyebrow was the single element deciding how light the
     scrim could go - it capped it while everything else had room. Deepened to
     the same accent used by the row labels so the wash can come further back. */
  .contact .tag{color:#63242F;}

  /* ==========================================================================
     HERO INTRO FILM
     The hero is now a one-shot film: top-down aerial, plunge, settle at street
     level. The line writes out over the aerial, the logo resolves as the shot
     settles, then the film pulls back below the header, revealing the nav and
     the scroll cue.

     Everything is gated on .intro-armed, which JS adds only once it knows the
     film can actually play. Without scripting, or if the video never loads, the
     page renders in its finished state and nothing is hidden.
     ========================================================================== */

  .hero-stick{position:relative;}
  .intro-line{position:relative; z-index:3; margin:0; max-width:min(1100px,88vw);
              text-align:center; font-family:var(--f-ui); font-weight:400;
              font-size:clamp(15px,2.05vw,30px); line-height:1.5;
              letter-spacing:.055em; margin-right:-.055em; color:#fff;
              text-shadow:0 2px 24px rgba(8,16,28,.55);}
  .intro-line .c{display:inline;}
  .intro-logo{position:absolute; left:50%; top:50%; z-index:4;
              width:clamp(190px,26vw,420px); height:auto;
              transform:translate(-50%,-50%) scale(.94);
              filter:drop-shadow(0 6px 34px rgba(8,16,28,.5));}

  /* ---- armed: the film owns the screen ---- */
  .intro-armed .hero-bg-stick{top:0; height:100svh;}
  .intro-armed #hdr{opacity:0; pointer-events:none;}
  .intro-armed .hero-scroll{opacity:0;}
  .intro-armed .intro-logo{opacity:0;}
  .intro-armed .intro-line .c{opacity:0;}
  /* a touch more wash while the line is on screen, so it reads over the city */
  .intro-armed .hero-veil{opacity:1;}

  /* JS writes these as it goes; the transitions do the easing */
  .intro-armed .intro-line .c{transition:opacity .45s var(--ease);}
  .intro-armed .intro-line{transition:opacity .5s var(--ease);}
  .intro-armed .intro-logo{transition:opacity .9s var(--ease), transform .9s cubic-bezier(.16,.9,.3,1);}
  .intro-armed #hdr,
  .intro-armed .hero-scroll{transition:opacity .8s var(--ease) .1s;}

  /* ---- released: the film pulls back below the header ---- */
  .intro-armed.intro-out .hero-bg-stick{top:var(--hdr-h,89px);
                                        height:calc(100svh - var(--hdr-h,89px));
                                        transition:top 1.05s cubic-bezier(.16,.9,.3,1),
                                                   height 1.05s cubic-bezier(.16,.9,.3,1);}
  .intro-armed.intro-out .hero-stick{padding-top:var(--hdr-h,89px);
                                     transition:padding-top 1.05s cubic-bezier(.16,.9,.3,1);}
  .intro-armed.intro-out #hdr{opacity:1; pointer-events:auto;}
  .intro-armed.intro-out .hero-scroll{opacity:1;}

  /* Motion is the whole point of this sequence, so under reduced-motion it is
     skipped outright rather than shortened: final state, no plunge. */
  @media (prefers-reduced-motion:reduce){
    .intro-armed .intro-line .c,
    .intro-armed .intro-logo,
    .intro-armed #hdr,
    .intro-armed .hero-scroll{opacity:1 !important; transition:none !important;}
    .intro-armed .intro-logo{transform:translate(-50%,-50%) scale(1) !important;}
  }

  /* Measured on the film's own frames: white type scored 2.40:1 on the aerial
     and the white lockup 2.24:1 on the settled street shot - both well under
     AA. The hero veil is a WHITE wash, built for navy type, so the intro
     follows the rest of the site and goes navy. */
  .intro-line{color:var(--color-charcoal);
              /* A tight, near-opaque halo does the work: it lifts the pixels
                 immediately around each glyph, which is where the worst
                 contrast sits when the line crosses a dark building. */
              text-shadow:0 0 3px rgba(255,255,255,.96), 0 0 8px rgba(255,255,255,.9),
                          0 0 22px rgba(255,255,255,.75), 0 1px 34px rgba(255,255,255,.6);}
  .intro-logo{filter:drop-shadow(0 4px 26px rgba(255,255,255,.6));}

  /* No white wash over the hero film - the footage plays clean. Only a fade at
     the very bottom survives, and solely so the stats band below still has
     something to sit on; without it that text would land straight on moving
     video. The central radial that used to hold the old wordmark is gone. */
  .hero-veil{background:linear-gradient(180deg, transparent 0%, transparent 62%,
                                        rgba(245,248,250,.34) 82%,
                                        rgba(245,248,250,.86) 94%,
                                        rgba(245,248,250,1) 100%) !important;}

  /* Measured against the bare footage (no wash): the line and the logo both
     clear AA in the deep navy, but the scroll cue sits across the road, whose
     luminance runs from 0.09 on the asphalt to 0.73 in the sun - no single ink
     colour can clear both. So it carries its own halo and a light rail track
     rather than depending on whatever is behind it. */
  .intro-line{color:var(--color-midnight);}
  .hero-scroll .hs-word{color:var(--color-midnight);
                        text-shadow:0 0 3px rgba(255,255,255,.97), 0 0 9px rgba(255,255,255,.92),
                                    0 0 20px rgba(255,255,255,.75);}
  .hero-scroll .hs-rail{background:rgba(255,255,255,.62);
                        box-shadow:0 0 10px rgba(255,255,255,.6);}
  /* "DEVELOPMENT" is the lighter slate of the lockup and it was disappearing
     into the bright sky on the settled frame. A tight white glow lifts it
     without putting a wash back over the footage. */
  .intro-logo{filter:drop-shadow(0 0 4px rgba(255,255,255,.95))
                     drop-shadow(0 0 14px rgba(255,255,255,.85))
                     drop-shadow(0 3px 30px rgba(255,255,255,.6));}

  /* ==========================================================================
     HERO INTRO - clean footage, darkened, white type
     No white wash anywhere, including the bottom. The film carries a slight
     dark grade instead, which is what lets the type be white and still clear
     AA over a bright daylight city.
     ========================================================================== */
  .hero-veil{background:linear-gradient(180deg, rgba(8,16,28,.40) 0%,
                                         rgba(8,16,28,.24) 26%,
                                         rgba(8,16,28,.22) 62%,
                                         rgba(8,16,28,.46) 100%) !important;}

  /* Four lines, typed, the former kicker folded in as the bold first line.
     The block is centred as a whole (place-items:center on .hero-stick) but
     the text inside is LEFT aligned, so every line starts at the same edge -
     it reads as one typed paragraph rather than four lines each drifting to
     their own centre. The block itself is sized to its widest line from the
     first frame - every character already sits in the DOM, opacity is all
     that changes - so this is static, not something that shifts as it types. */
  .intro-line{color:#fff !important; display:inline-block; text-align:left;
              font-size:clamp(19px,2.5vw,40px); line-height:1.42;
              letter-spacing:.02em; margin-right:0;
              text-shadow:0 2px 20px rgba(8,16,28,.75), 0 0 3px rgba(8,16,28,.45) !important;}
  .intro-line .l{display:block; white-space:nowrap; font-weight:400; font-style:normal;}

  /* Blinking caret, following the last typed character - site.js keeps the
     line's .typing class on once it finishes, so this keeps blinking through
     the rest of the hold rather than vanishing the moment typing ends. */
  .intro-line .caret{display:inline-block; width:.075em; height:1.02em;
                     background:#fff; vertical-align:-.14em; margin-left:.07em;
                     opacity:0; box-shadow:0 0 8px rgba(8,16,28,.5);}
  .intro-line .l.typing .caret{opacity:1; animation:caretBlink 1.05s steps(1,end) infinite;}
  .intro-line .l.done .caret{opacity:0;}
  @keyframes caretBlink{0%,50%{opacity:1} 50.01%,100%{opacity:0}}

  /* the reveal is the icon on its own now, not the full lockup - plain navy
     artwork, no drop-shadow and no shade pool behind it. */
  .intro-logo{width:clamp(88px,10.5vw,176px); filter:none !important;}

  /* scroll cue sits on the darkened footage too - plain white, no halo. */
  .hero-scroll .hs-word{color:#fff !important; text-shadow:none !important;}
  .hero-scroll .hs-rail{background:rgba(255,255,255,.3) !important; box-shadow:none !important;}
  .hero-scroll .hs-rail i{background:#fff;}

  /* Bottom of the frame is sunlit road, which is where the scroll cue sits and
     where light ink measures worst. Deepening only that end of the grade lifts
     it without a shadow or shade pool behind either element. */
  .hero-veil{background:linear-gradient(180deg, rgba(8,16,28,.40) 0%,
                                         rgba(8,16,28,.24) 26%,
                                         rgba(8,16,28,.26) 58%,
                                         rgba(8,16,28,.50) 82%,
                                         rgba(8,16,28,.66) 100%) !important;}
  /* The lockup holds in the centre of the screen while the film stays behind
     it, then releases as the stats band rises. Sticky needs somewhere to
     travel, and .hero was exactly the height of .hero-stick - no slack, so
     the mark scrolled straight off. The extra height on .hero is that runway;
     .hero-stick pins for it, and site.js fades the mark out as the stats
     close in (see moveIntroLogo). */
  .hero{height:172svh;}
  .hero-stick{position:sticky; top:0; height:100svh;}
  /* The stats are pulled back up by exactly the runway added above, so the
     page is laid out as if the hero were still one screen tall - nothing
     below moves. What changes is that the band now scrolls up ACROSS the
     pinned lockup instead of waiting until after it. Without this the stats
     sat below the runway and could never approach the mark: the gap between
     them froze the moment the pin released, and the fade never fired.
     They already paint no background of their own, so riding over the film
     is the treatment they were built for. */
  .stats{margin-top:-50svh;}
  .intro-logo{z-index:4; will-change:opacity;}
  @media (max-width:1024px){
    /* no pinning on a phone - it just means a lot of thumb travel against a
       still frame */
    .hero{height:auto;}
    .hero-stick{position:relative; height:100svh;}
    .stats{margin-top:0;}
  }

  /* ==========================================================================
     SERVICES PAGE - cream paper treatment
     Scoped to body.svc-cream so nothing here leaks onto the other pages,
     which keep their own white header and light footer. This page runs the
     same warm cream paper texture start to finish, header and footer
     included: a debossed, oversized mark stamped behind the headline, then
     an alternating left/right timeline for the seven services, reusing the
     home page's own "What we do" copy rather than the old tabbed panels
     this page used to carry. All type on the page runs navy, not the
     brass/maroon accent used elsewhere on the site.
     ========================================================================== */
  /* Real photo of the physical debossed card stock (not a tiled procedural
     texture) - held fixed and covering the viewport so the grain stays put
     while the page scrolls past it, the same way a sheet of paper would sit
     behind the content. Fixed attachment is dropped on narrower/touch
     viewports below, where it's both unsupported (iOS Safari) and a known
     scroll-perf cost for no visual benefit. */
  body.svc-cream{background:#E4DDD1 url('svc-paper-texture.jpg') no-repeat center center/cover fixed;}
  @media (max-width:820px){
    body.svc-cream{background-attachment:scroll;}
  }

  /* ---- header: transparent over the paper, navy ink (the site default -
     .nav-links/.burger/.nav-cta all already read navy-on-light, so the only
     thing this page needs to change is dropping the header's own solid
     white fill so the paper texture shows through underneath it). ---- */
  body.svc-cream header{background:transparent; box-shadow:none;}

  /* ---- hero: stamped mark + left-aligned headline ----
     Laid out like the About page's own hero now - left-aligned tag/heading/
     lede in the page's normal .wrap (not a narrowed or centered column),
     h1 sized and weighted the same as .page-hero h1, with one word set in
     the same italic-serif em treatment About uses for "excellence." The
     mark itself is the client's own photo of the physical debossed card
     stock, used untouched (no re-processing) so the paper grain, lighting
     and mark all come from the same shot - it's placed as the section's own
     cover background rather than composited from separate layers, so
     there's nothing for us to get out of alignment. It's the same paper/
     lighting as svc-paper-texture.jpg (the plain sheet used for the rest of
     the page), just with the mark pressed into it, so the two read as one
     continuous surface even though they're technically two different
     photos. */
  .svc-hero{position:relative; overflow:hidden; min-height:92svh;
            display:grid; align-content:center;
            padding:clamp(150px,20vh,230px) 0 clamp(56px,8vh,96px);}
  /* The emboss photo lives on ::before instead of directly on .svc-hero, and
     is masked to fade out before it reaches the bottom edge. Because
     .svc-hero itself has no image of its own beneath that fade, the body's
     own fixed paper-texture background (same sheet, further downscroll)
     shows through the transparent part directly - so the seam isn't two
     images trying to match each other, it's one image handing off to
     itself. */
  /* The mark was a 2.1 MB PNG, which is the wrong container for what is
     really a photograph - re-encoded it is 183 KB as JPEG and 65 KB as WebP.
     The shorthand below carries the JPEG so every browser gets a working
     image; the image-set() line then upgrades to WebP where supported, and
     is simply ignored where it isn't. */
  .svc-hero::before{content:''; position:absolute; inset:0; z-index:0;
                    pointer-events:none;
                    background:url('svc-hero-emboss.jpg') no-repeat center center/cover;
                    background-image:-webkit-image-set(url('svc-hero-emboss.webp') type('image/webp'),
                                                       url('svc-hero-emboss.jpg')  type('image/jpeg'));
                    background-image:image-set(url('svc-hero-emboss.webp') type('image/webp'),
                                               url('svc-hero-emboss.jpg')  type('image/jpeg'));
                    -webkit-mask-image:linear-gradient(to bottom, #000 78%, transparent 100%);
                    mask-image:linear-gradient(to bottom, #000 78%, transparent 100%);}
  .svc-hero .wrap{position:relative; z-index:1;}
  .svc-hero .tag{color:var(--color-navy); opacity:.7;}
  .svc-hero h1{color:var(--color-navy); font-family:var(--f-display);
               font-weight:600; font-size:clamp(38px,6.4vw,86px);
               letter-spacing:var(--ls-head); line-height:1.04; margin:18px 0 0;}
  .svc-hero h1 em.hero-serif-em{font-family:Georgia,'Times New Roman',serif;
                                font-style:italic; font-weight:400; color:inherit;}
  .svc-hero .lede{color:var(--color-navy); opacity:.9;
                  margin-top:clamp(16px,2.4vh,26px); max-width:58ch;}
  /* ---- scroll progress: dot + growing line, fixed to the viewport ----
     Fixed (not absolute inside the hero) on purpose - it has to track the
     whole page's scroll position, including reaching "the bottom," which
     only means something if it stays on screen well past the hero. The
     line's height is driven by JS (see initSvcScrollTrack in site.js) as a
     direct function of scroll position: 0% scrolled = short line with the
     arrow showing, growing continuously as the page scrolls, arrow fading
     out once scroll reaches the very bottom, and everything reverses on the
     way back up since it's tied to position rather than a one-shot
     animation. */

  /* ---- timeline: alternating left/right, connected by a center line ---- */
  .svc-timeline{position:relative;
                padding:clamp(30px,5vh,60px) 0 clamp(90px,12vh,150px);}
  .svc-timeline .wrap{position:relative;}
  /* The line has no resting length - it starts at 0 and is drawn entirely by
     the descending arrow as you scroll (see the timeline progress block in
     site.js), so there is no pre-existing rail behind the services for the
     arrow to travel along. Height and the arrow's offset are written in the
     same frame from the same value, so the arrowhead always sits exactly at
     the line's tip rather than trailing or overlapping it. No CSS
     transition on either, for the same reason: it would let one lag a frame
     behind the other during a fast scroll. */
  .tl-line{position:absolute; top:0; left:50%; width:2px; height:0;
           background:var(--brass); transform:translateX(-1px);}
  .tl-arrow{position:absolute; top:0; left:50%; width:0; height:0;
            margin-left:-5px; z-index:3;
            border-left:5px solid transparent; border-right:5px solid transparent;
            border-top:7px solid var(--brass);
            transition:opacity .3s ease;}
  .tl-arrow.tl-arrow-done{opacity:0;}
  /* align-items:center so each service block sits vertically centred on its
     own dot rather than starting at the top of the row. */
  .tl-row{position:relative; display:grid; grid-template-columns:1fr 1fr;
          align-items:center;
          column-gap:clamp(48px,7vw,110px); padding:clamp(38px,5.5vh,68px) 0;}
  /* Every dot is the same solid maroon - no navy, no ring/halo, and no
     start/end special-casing, so the whole graphic (dots, line, arrow) is
     one colour. Each begins hidden and is switched on by the scroll handler
     the moment the drawing tip passes it, so the dots appear with the line
     instead of sitting there waiting for it. */
  .tl-dot{position:absolute; left:50%; top:50%; z-index:2; width:10px; height:10px;
          border-radius:50%; background:var(--brass);
          opacity:0; transform:translate(-50%,-50%) scale(.35);
          transition:opacity .3s ease, transform .3s var(--spring);}
  .tl-dot.tl-dot-on{opacity:1; transform:translate(-50%,-50%) scale(1);}
  /* Centred within its own half of the row, so the two columns read as
     balanced about the line rather than one hugging it and one pushed out
     to the page edge. */
  .tl-item{max-width:48ch; justify-self:center;}
  /* Each service starts muted gray and switches to full navy as it scrolls
     into the active reading zone (.tl-row.tl-active, toggled by an
     IntersectionObserver in site.js) - a soft color transition rather than
     an instant swap, and reversible, so scrolling back up returns a row to
     gray once it's no longer the one being read. */
  .tl-item h3{font-family:var(--f-ui); font-weight:400; color:var(--muted-l);
              font-size:clamp(23px,2.5vw,32px); letter-spacing:0; margin-bottom:16px;
              transition:color .6s ease;}
  .tl-item p{font-family:var(--f-text); font-size:clamp(15px,1.35vw,17px);
             line-height:1.72; color:var(--muted-l); opacity:1;
             transition:color .6s ease;}
  .tl-row.tl-active .tl-item h3{color:var(--color-navy);}
  .tl-row.tl-active .tl-item p{color:var(--color-navy); opacity:.9;}
  .tl-row.tl-left .tl-item{grid-column:1;}
  .tl-row.tl-right .tl-item{grid-column:2;}

  @media (max-width:820px){
    .svc-hero{min-height:70svh; padding-top:clamp(110px,15vh,150px);}
    /* The rail and the dots hang off different ancestors: .tl-line and
       .tl-arrow are positioned against .svc-timeline .wrap, whose padding box
       starts at the very edge of the page, while .tl-dot is positioned
       against its own .tl-row, which already sits inside that padding. On
       desktop both use left:50% so they coincide anyway, but at this
       breakpoint a bare left:6px on all three put the dots ~20px to the right
       of the line they are supposed to sit on. Adding the wrap's own gutter
       back onto the rail keeps the two in the same column. */
    .tl-line,
    .tl-arrow{left:calc(clamp(20px,4vw,64px) + 6px);}
    .tl-dot{left:6px;}
    .tl-row{grid-template-columns:1fr; padding:26px 0 26px clamp(28px,7vw,40px);}
    /* single column here, so the centring used on desktop would just leave
       ragged indents - items go back to filling their column */
    .tl-item{justify-self:stretch;}
    .tl-row.tl-left .tl-item,
    .tl-row.tl-right .tl-item{grid-column:1;}
  }

  /* ---- nationwide coverage: full-bleed photo that runs into the footer ----
     The photo's own bottom edge is black, and body.svc-cream's footer is set
     to that same black below, so the picture appears to continue all the way
     down past the footer text rather than stopping at a seam. object-fit
     covers the section at any aspect ratio, and object-position keeps the
     mountain (the subject) in frame as the section gets narrower. */
  .svc-reach{position:relative; overflow:hidden; isolation:isolate;
             display:grid; align-content:center;
             min-height:min(78svh,700px);
             padding:clamp(80px,12vh,140px) 0 clamp(90px,14vh,160px);}
  .svc-reach-bg{position:absolute; inset:0; z-index:0; display:block;}
  .svc-reach-bg img{width:100%; height:100%; object-fit:cover;
                    object-position:center 32%; display:block;}
  /* The photo's sky is pale, so the type needs a scrim to clear contrast -
     weighted to the top, where the copy sits, and fading out lower down so
     the darkest part of the picture is left alone. */
  .svc-reach::before{content:''; position:absolute; inset:0; z-index:1;
                     pointer-events:none;
                     background:linear-gradient(180deg, rgba(8,10,14,.50) 0%,
                                                        rgba(8,10,14,.40) 42%,
                                                        rgba(8,10,14,.16) 74%,
                                                        rgba(8,10,14,0) 100%);}
  /* Guarantees the hand-off to the black footer at every viewport. The photo
     is object-fit:cover, so on narrow screens its own (already black) bottom
     band can be cropped away, which would leave the mid-tone forest meeting
     the footer at a visible seam. Fading the last slice to the exact footer
     black means the two always meet on the same colour regardless of crop. */
  .svc-reach::after{content:''; position:absolute; left:0; right:0; bottom:0;
                    height:22%; z-index:1; pointer-events:none;
                    background:linear-gradient(180deg, rgba(0,0,0,0), #000 100%);}
  /* Photo caption, top-right - the shared .hs-loc treatment from the hero
     slideshows, just repositioned for this section (the base rule pins it
     against a 56px-tall scrubber bar that doesn't exist here). */
  .svc-reach .hs-loc{top:clamp(26px,4.5vh,52px); transform:none;
                     display:flex; align-items:center; gap:6px; z-index:3;}
  .svc-reach .hs-loc-pin{width:12px; height:12px; flex:none; color:#fff;
                         filter:drop-shadow(0 1px 4px rgba(8,16,28,.7));}
  .svc-reach .wrap{position:relative; z-index:2; text-align:center;}
  .svc-reach h2{font-family:var(--f-display); font-weight:600; color:#fff;
                font-size:clamp(34px,5.4vw,72px); letter-spacing:var(--ls-head);
                line-height:1.06; margin:0;}
  .svc-reach p{color:rgba(255,255,255,.94); font-family:var(--f-text);
               font-size:clamp(16px,1.5vw,19px); line-height:1.6;
               max-width:46ch; margin:18px auto 0;}
  .svc-reach .btn-primary{margin-top:clamp(26px,3.6vh,40px);}

  /* ---- footer: black, picking up exactly where the coverage photo above it
     ends. The photo's bottom edge is effectively pure black (sampled: the
     final pixel row medians 0,0,0), so #000 here reads as the same surface
     continuing rather than a separate band, and the border-top that would
     otherwise draw a visible seam is dropped. Type and lockup go white for
     contrast against it. ---- */
  body.svc-cream footer{background:#000; border-top:0; color:#fff;}
  body.svc-cream footer .f-nav a{color:rgba(255,255,255,.86);}
  body.svc-cream footer .f-nav a:hover{color:#fff;}
  body.svc-cream footer .f-end{border-top-color:rgba(255,255,255,.16);}
  body.svc-cream footer .f-end p{color:rgba(255,255,255,.72);}

  /* ==========================================================================
     CONTACT PAGE - flat navy, white type
     Every contact CTA across the site points here instead of jumping to the
     home page's Let's Talk section; that pinned sequence still owns the home
     page form. Scoped to body.contact-navy so none of this reaches the other
     pages. The form deliberately reuses #contactForm / #formStatus, the same
     ids the home page uses, so site.js's existing Netlify submit handler
     drives it with no extra JS.
     ========================================================================== */
  body.contact-navy{background:var(--color-navy); color:#fff;
                    min-height:100svh; display:flex; flex-direction:column;}

  /* Header and footer both sit directly on the navy - no white plate, no
     hairlines, and the nav ink flips to white. */
  body.contact-navy header{background:transparent; box-shadow:none;}
  body.contact-navy .nav-links a{color:rgba(255,255,255,.86);}
  body.contact-navy .nav-links a:hover,
  body.contact-navy .nav-links a.here{color:#fff;}
  body.contact-navy .burger i{background:#fff;}
  /* the pill would be navy-on-navy, so it becomes a white outline instead */
  body.contact-navy .nav-cta{background:transparent; color:#fff;
                             border:1px solid rgba(255,255,255,.45);}
  body.contact-navy .nav-cta:hover{background:#fff; color:var(--color-navy);
                                   border-color:#fff; box-shadow:none;}
  body.contact-navy footer{background:transparent; color:#fff;
                           border-top:1px solid rgba(255,255,255,.16);}
  body.contact-navy footer .f-nav a{color:rgba(255,255,255,.86);}
  body.contact-navy footer .f-nav a:hover{color:#fff;}
  body.contact-navy footer .f-end{border-top-color:rgba(255,255,255,.16);}
  body.contact-navy footer .f-end p{color:rgba(255,255,255,.72);}

  /* flex:1 so the footer is pushed to the bottom of the viewport even when
     the form is short enough not to fill the screen */
  .ct-page{flex:1 0 auto;
           padding:clamp(140px,18vh,200px) 0 clamp(70px,10vh,110px);}
  .ct-grid{display:grid; grid-template-columns:minmax(0,5fr) minmax(0,6fr);
           gap:clamp(44px,7vw,110px); align-items:start;}

  .ct-aside .tag{color:rgba(255,255,255,.7);}
  .ct-aside h1{font-family:var(--f-display); font-weight:600; color:#fff;
               font-size:clamp(38px,5.4vw,72px); letter-spacing:var(--ls-head);
               line-height:1.04; margin:16px 0 0;}
  .ct-lede{color:rgba(255,255,255,.82); font-family:var(--f-text);
           font-size:clamp(15px,1.4vw,18px); line-height:1.65;
           max-width:40ch; margin-top:18px;}

  .ct-people{list-style:none; margin:clamp(34px,5vh,52px) 0 0; padding:0;
             display:flex; flex-direction:column; gap:clamp(20px,2.8vh,30px);}
  .ct-people li{display:flex; flex-direction:column; gap:3px;
                padding-top:clamp(16px,2.2vh,22px);
                border-top:1px solid rgba(255,255,255,.18);}
  .ct-people b{font-family:var(--f-ui); font-weight:500; font-size:17px;
               color:#fff; letter-spacing:.01em;}
  .ct-people span{font-family:var(--f-ui); font-size:11px; font-weight:400;
                  letter-spacing:.16em; margin-right:-.16em;
                  text-transform:uppercase; color:rgba(255,255,255,.6);}
  .ct-people a{font-family:var(--f-text); font-size:15.5px; margin-top:5px;
               color:rgba(255,255,255,.9); border-bottom:1px solid rgba(255,255,255,.28);
               align-self:flex-start; transition:color .3s, border-color .3s;}
  .ct-people a:hover{color:#fff; border-bottom-color:#fff;}

  /* ---- form ----
     Visible <label>s rather than placeholder-only fields: placeholders vanish
     the moment someone starts typing, which on a six-field form leaves no way
     to tell what a half-filled row was asking for. */
  .ct-form{display:grid; grid-template-columns:1fr 1fr;
           column-gap:clamp(14px,1.6vw,22px); row-gap:clamp(16px,2.2vh,22px);}
  .ct-field{display:flex; flex-direction:column; gap:8px; min-width:0;}
  .ct-full{grid-column:1 / -1;}
  .ct-form label{font-family:var(--f-ui); font-size:11px; font-weight:400;
                 letter-spacing:.16em; margin-right:-.16em; text-transform:uppercase;
                 color:rgba(255,255,255,.72);}
  /* Underline fields rather than boxes: no fill, no radius, and only a
     bottom rule. That rule is the sole thing marking the input's extent, so
     it stays at .42 alpha to clear the 3:1 WCAG 1.4.11 minimum against the
     navy (measured 3.40:1; a fainter .28 hairline gave only 2.31:1). */
  .ct-form input,
  .ct-form textarea{width:100%; font-family:var(--f-text); font-weight:300;
                    font-size:16px; color:#fff; padding:10px 2px;
                    border-radius:0; outline:none; background:transparent;
                    border:0; border-bottom:1px solid rgba(255,255,255,.42);
                    transition:border-color .3s, box-shadow .3s;}
  .ct-form textarea{resize:vertical; min-height:96px;}
  .ct-form input:hover,
  .ct-form textarea:hover{border-bottom-color:rgba(255,255,255,.62);}
  /* focus ring is a second, heavier rule on the same edge - a box-shadow
     halo would re-introduce exactly the boxed look this is replacing */
  .ct-form input:focus,
  .ct-form textarea:focus{border-bottom-color:#fff;
                          box-shadow:0 1px 0 0 #fff;}
  /* No :invalid border tint here. These fields carry real <label>s rather
     than placeholder attributes, and :placeholder-shown only ever matches an
     input that HAS a placeholder - so :invalid:not(:placeholder-shown) was
     true for every empty required field the moment the page loaded, marking
     the whole form red before anyone had typed a thing. Native validation
     still reports on submit. */
  /* two classes deep so it beats the shared single-class .send rules (the
     button carries both, because site.js looks it up by .send) */
  .ct-form .ct-send{grid-column:1 / -1; justify-self:start; align-self:start;
           margin-top:clamp(6px,1vh,12px); padding:17px 52px; border-radius:999px;
           background:#fff; color:var(--color-navy); box-shadow:none;
           font-family:var(--f-ui); font-weight:500; font-size:10.5px;
           letter-spacing:.3em; margin-right:-.3em; text-transform:uppercase;
           cursor:pointer; border:0;
           transition:transform .4s var(--spring), background .3s, color .3s;}
  .ct-form .ct-send:hover{transform:translateY(-2px); background:var(--brass);
                          color:#fff; box-shadow:none;}
  .ct-form .ct-send[disabled]{opacity:.6; cursor:default; transform:none;}
  .ct-form .f-status{grid-column:1 / -1; color:rgba(255,255,255,.9);}
  .ct-form .f-status.ok{color:#fff;}
  /* the shared .f-status.err colour is the maroon brand tone, which is far too
     dark to read on navy - the error state gets a light tint here instead */
  .ct-form .f-status.err{color:#FFC2CC;}
  .ct-form .f-status a{color:#fff; border-bottom-color:rgba(255,255,255,.5);}

  @media (max-width:900px){
    .ct-grid{grid-template-columns:1fr; gap:clamp(38px,6vh,54px);}
  }
  @media (max-width:560px){
    .ct-form{grid-template-columns:1fr;}
  }
