/* ---------- FONTS (self-hosted, latin subset, SIL OFL — see fonts/) ---------- */
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:300;font-display:swap;src:url(../fonts/cormorant-garamond-latin-300-normal.woff2) format('woff2');}
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/cormorant-garamond-latin-400-normal.woff2) format('woff2');}
@font-face{font-family:'Cormorant Garamond';font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/cormorant-garamond-latin-400-italic.woff2) format('woff2');}
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/cormorant-garamond-latin-500-normal.woff2) format('woff2');}
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:600;font-display:swap;src:url(../fonts/cormorant-garamond-latin-600-normal.woff2) format('woff2');}
@font-face{font-family:'Inter';font-style:normal;font-weight:300;font-display:swap;src:url(../fonts/inter-latin-300-normal.woff2) format('woff2');}
@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/inter-latin-400-normal.woff2) format('woff2');}
@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/inter-latin-500-normal.woff2) format('woff2');}
@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(../fonts/inter-latin-600-normal.woff2) format('woff2');}

:root{
  --bg:#F8F7F4;
  --bg-2:#FFFFFF;
  --dark:#0D0D0D;
  --accent:#B08B57;
  --accent-hover:#D6B37A;--accent-bright:#E8CE9C;
  --border:rgba(0,0,0,.08);
  --text:#111111;
  --text-2:#666666;
  --glow-1:rgba(176,139,87,.20);
  --glow-2:rgba(255,255,255,.35);
  --glass-bg:rgba(255,255,255,.55);
  --glass-border:rgba(255,255,255,.55);
  --glass-bg-dark:rgba(20,18,15,.45);
  /* Shared tokens for new and refactored rules. Existing rules still use
     literal values in places; prefer these when touching them. */
  --font-serif:'Cormorant Garamond',Georgia,'Times New Roman',serif;
  --font-sans:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;
  --ease-out:cubic-bezier(.16,1,.3,1);
  --ease-soft:cubic-bezier(.22,1,.36,1);
  --gutter:48px;
  --gutter-sm:24px;
  --focus:2px solid var(--accent);
}
*{margin:0;padding:0;box-sizing:border-box;}
/* overflow-x:hidden turns <body> into a scroll container, which silently
   breaks position:sticky for everything inside it — that is what kept the
   pinned hero from pinning. overflow-x:clip suppresses horizontal overflow
   exactly the same way WITHOUT creating a scroll container, so sticky keeps
   working. Falls back to hidden on engines without clip support. */
html,body{background:var(--bg);color:var(--text);overflow-x:hidden;}
@supports (overflow-x: clip){ html,body{overflow-x:clip;} }
body{font-family:var(--font-sans);font-weight:300;-webkit-font-smoothing:antialiased;cursor:auto;}
::selection{background:var(--accent);color:#fff;}
a{color:inherit;text-decoration:none;}
img{display:block;max-width:100%;}
h1,h2,h3,h4{font-family:var(--font-serif);font-weight:400;letter-spacing:.01em;}
.eyebrow{font-family:var(--font-sans);text-transform:uppercase;letter-spacing:.28em;font-size:11px;color:var(--text-2);font-weight:500;}
.lenis{scroll-behavior:auto!important;}

/* ---------- AMBIENT LIGHT / GRAIN ----------
   Two fixed, decorative overlays that sit above every section
   regardless of its own background colour (they're placed early in
   the DOM but pulled above normal-flow content via z-index). The
   glow drifts almost imperceptibly slowly; the grain is a static
   noise texture blended with soft-light/overlay so it never reads
   as "dirty", just adds a touch of filmic depth. Both are inert —
   pointer-events:none — and both respect prefers-reduced-motion. */
.ambient-glow{position:fixed;inset:0;z-index:2;pointer-events:none;mix-blend-mode:soft-light;opacity:.7;
  background-image:
    radial-gradient(38vmax 38vmax at 10% 15%, var(--glow-1), transparent 70%),
    radial-gradient(42vmax 42vmax at 92% 88%, var(--glow-2), transparent 72%);
  background-repeat:no-repeat;
  animation:ambientDrift 34s ease-in-out infinite alternate;
  will-change:background-position;}
@keyframes ambientDrift{
  0%{background-position:0% 0%, 100% 100%;}
  50%{background-position:5% 7%, 94% 92%;}
  100%{background-position:-3% 4%, 103% 96%;}
}
.ambient-grain{position:fixed;inset:0;z-index:3;pointer-events:none;opacity:.045;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");}
@media (max-width:768px){.ambient-glow{opacity:.4;}}
@media (prefers-reduced-motion: reduce){.ambient-glow{animation:none;}}
/* Two full-viewport fixed layers using mix-blend-mode sit above every
   section. On desktop GPUs that's cheap. On iOS it forces Safari to
   re-composite the whole page against them on every single scroll frame,
   which is what makes scrolling feel heavy and stuttery on an iPhone
   while being smooth on a laptop. Touch devices drop the grain entirely
   and keep a static, non-blended glow — visually near-identical at phone
   size, and it hands the scroll back to the compositor. */
@media (hover:none),(pointer:coarse){
  .ambient-grain{display:none;}
  .ambient-glow{mix-blend-mode:normal;animation:none;opacity:.28;will-change:auto;}
}

/* ---------- GLASS UTILITY ---------- */
.glass{background:var(--glass-bg);-webkit-backdrop-filter:blur(14px) saturate(160%);backdrop-filter:blur(14px) saturate(160%);-webkit-backdrop-filter:blur(14px) saturate(160%);border:1px solid var(--glass-border);}

/* ---------- CURSOR ----------
   Progressive enhancement: the system cursor (cursor:auto above) stays
   visible by default. Only once JS confirms the custom cursor is up and
   running does it add .cursor-ready to <html>, which is what actually
   hides the native pointer. If JS never runs, errors out, or the device
   uses a coarse/touch pointer, the system cursor is simply never hidden. */
.cursor{position:fixed;top:0;left:0;width:74px;height:74px;border-radius:50%;background:#fff;pointer-events:none;z-index:10050;mix-blend-mode:difference;opacity:0;}
html.cursor-ready body{cursor:none;}
html.cursor-ready .cursor{opacity:1;}
html.cursor-ready .pcard,html.cursor-ready .btn,html.cursor-ready .pd-back,html.cursor-ready .pd-gallery{cursor:none;}
@media (max-width:1023px),(hover:none),(pointer:coarse){
  .cursor{display:none!important;}
  html.cursor-ready body{cursor:auto;}
  html.cursor-ready .pcard,html.cursor-ready .btn,html.cursor-ready .pd-back{cursor:pointer;}
}

/* ---------- LOADER / CINEMATIC INTRO ---------- */
#loader{position:fixed;inset:0;background:#0A0A0A;z-index:10000;display:flex;flex-direction:column;align-items:center;justify-content:center;overflow:hidden;}
#loader .igrid{position:absolute;inset:0;opacity:.045;background-image:linear-gradient(rgba(255,255,255,.6) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.6) 1px,transparent 1px);background-size:64px 64px;pointer-events:none;}
#loader .ivign{position:absolute;inset:0;background:radial-gradient(ellipse at 50% 50%,transparent 35%,rgba(0,0,0,.65) 100%);pointer-events:none;}
#loader .intro-mark{position:relative;display:flex;align-items:baseline;gap:.5em;font-family:var(--font-serif);font-weight:400;font-size:clamp(58px,12vw,180px);color:#fff;line-height:1;}
#loader .intro-mark .ch{display:inline-block;opacity:0;transform:translateY(46px);filter:blur(10px);}
#loader .intro-mark .plus{display:inline-block;opacity:0;transform:translateY(46px) rotate(-90deg);filter:blur(10px);color:var(--accent);font-size:.6em;font-weight:300;transform-origin:10% 50%;}
#loader .intro-sub{position:relative;margin-top:24px;opacity:0;transform:translateY(14px);font-family:var(--font-sans);font-size:11px;letter-spacing:.55em;text-transform:uppercase;color:rgba(255,255,255,.55);}
#loader .bar{position:relative;margin-top:46px;width:220px;height:1px;background:rgba(255,255,255,.15);overflow:hidden;opacity:0;}
#loader .bar span{position:absolute;left:0;top:0;height:100%;width:100%;background:var(--accent);transform:scaleX(0);transform-origin:0 0;}
#loader .pct{position:relative;margin-top:16px;font-size:10px;color:rgba(255,255,255,.5);letter-spacing:.3em;opacity:0;}

/* ---------- LAYOUT HELPERS ---------- */
.wrap{max-width:1400px;margin:0 auto;padding:0 48px;}
@media (max-width:768px){.wrap{padding:0 24px;}}
section{position:relative;}

/* ---------- NAV ---------- */
header#nav{position:fixed;top:0;left:0;width:100%;z-index:700;padding:32px 48px;display:flex;justify-content:space-between;align-items:center;background:linear-gradient(180deg,rgba(0,0,0,.5) 0%,rgba(0,0,0,.18) 65%,transparent 100%);transition:padding .4s cubic-bezier(.16,1,.3,1),background .5s ease,backdrop-filter .5s ease,box-shadow .5s ease,border-color .5s ease;}
header#nav.scrolled{padding:18px 48px;background:rgba(10,9,8,.72);-webkit-backdrop-filter:blur(18px) saturate(160%);backdrop-filter:blur(18px) saturate(160%);-webkit-backdrop-filter:blur(18px) saturate(160%);border-bottom:1px solid rgba(255,255,255,.08);box-shadow:0 20px 50px rgba(0,0,0,.18);}
header#nav .logo{color:#fff;font-family:var(--font-serif);font-size:20px;letter-spacing:.35em;text-transform:uppercase;text-shadow:0 1px 12px rgba(0,0,0,.5);}
header#nav nav{display:flex;gap:44px;}
header#nav nav a{color:#fff;font-size:11px;letter-spacing:.22em;text-transform:uppercase;position:relative;text-shadow:0 1px 10px rgba(0,0,0,.5);}
header#nav nav a::after{content:'';position:absolute;left:0;bottom:-6px;width:0;height:1px;background:#fff;transition:width .4s cubic-bezier(.16,1,.3,1);}
header#nav nav a:hover::after{width:100%;}
header#nav .menu-btn{display:none;color:#fff;font-size:11px;letter-spacing:.22em;text-transform:uppercase;text-shadow:0 1px 10px rgba(0,0,0,.5);}
/* An overlay page (Project / About / Works / Careers) is open: the nav
   stays put on top of it, so give it the same glass treatment it gets
   when scrolled — the hero gradient it normally sits on is not there. */
body.pd-locked header#nav{padding:18px 48px;background:rgba(10,9,8,.72);
  -webkit-backdrop-filter:blur(18px) saturate(160%);backdrop-filter:blur(18px) saturate(160%);-webkit-backdrop-filter:blur(18px) saturate(160%);
  border-bottom:1px solid rgba(255,255,255,.08);box-shadow:0 20px 50px rgba(0,0,0,.28);}
@media (max-width:1023px){body.pd-locked header#nav{padding:24px;}}
/* .scrolled outranks the plain mobile header#nav rule, so phones were
   getting the desktop 48px side gutters once scrolled (the logo jumped in,
   and sat off the mobile menu's 24px grid). Mobile keeps 24px; the menu, when
   open, also gets the unscrolled row height so its hairline lines up. */
@media (max-width:1023px){
  header#nav.scrolled{padding:max(18px, env(safe-area-inset-top, 0px)) max(24px, env(safe-area-inset-right, 0px)) 18px max(24px, env(safe-area-inset-left, 0px));}
  body.menu-open header#nav{padding:max(24px, env(safe-area-inset-top, 0px)) max(24px, env(safe-area-inset-right, 0px)) 24px max(24px, env(safe-area-inset-left, 0px));}
}
/* Active section indicator. The underline is drawn on a pseudo-element so
   it can animate width without shifting the text, and it sits below the
   baseline rather than replacing the hover treatment. */
header#nav nav a{position:relative;}
header#nav nav a::after{
  content:'';position:absolute;left:0;right:0;bottom:-6px;height:1px;margin:0 auto;
  width:0;background:var(--accent);opacity:0;
  transition:width .4s cubic-bezier(.16,1,.3,1),opacity .3s ease;}
header#nav nav a.is-current{color:var(--accent-bright);}
header#nav nav a.is-current::after{width:100%;opacity:1;height:2px;bottom:-7px;
  background:var(--accent-bright);}
header#nav nav a.is-current:hover{color:var(--accent-bright);}
@media (prefers-reduced-motion: reduce){
  header#nav nav a::after{transition:none;}
}
@media (max-width:1023px){header#nav nav{display:none;}header#nav .menu-btn{display:block;}header#nav{padding:24px;}}

/* ---------- HERO ----------
   Full-bleed film with the headline over it. The sticky/pinned building
   composition and its sky, haze and cut-out layers were removed with it, so
   the hero is a single 100vh section again — no #heroWrap, no scroll
   distance to reserve. */
#hero{height:100vh;height:100dvh;width:100%;position:relative;overflow:hidden;
  background:var(--dark);}
#hero .media{position:absolute;inset:0;}
#hero .media img, #hero .media video{width:100%;height:100%;object-fit:cover;
  transform:scale(1.18);opacity:.85;will-change:transform;}
#hero .overlay{position:absolute;inset:0;will-change:opacity;
  background:linear-gradient(180deg,rgba(0,0,0,.35) 0%,rgba(0,0,0,.15) 40%,rgba(0,0,0,.55) 100%);}
#hero .content{position:relative;z-index:5;height:100%;display:flex;flex-direction:column;
  align-items:center;justify-content:center;text-align:center;color:#fff;padding:0 24px;
  will-change:transform,opacity;}
#hero .eyebrow-row{display:flex;gap:18px;align-items:center;margin-bottom:26px;
  color:rgba(255,255,255,.75);}
#hero .eyebrow-row span{font-size:11px;letter-spacing:.3em;text-transform:uppercase;}
#hero .eyebrow-row i{width:5px;height:5px;border-radius:50%;background:var(--accent);
  display:inline-block;}
#hero h1{font-size:clamp(36px,6vw,84px);line-height:.98;font-weight:400;overflow:hidden;}
#hero h1 .line{display:block;overflow:hidden;padding-bottom:.14em;}
#hero h1 .line span{display:inline-block;transform:translateY(110%);}
#hero .scroll-ind{position:absolute;bottom:40px;left:50%;transform:translateX(-50%);
  display:flex;flex-direction:column;align-items:center;gap:10px;
  color:rgba(255,255,255,.7);z-index:5;}
#hero .scroll-ind .line{width:1px;height:50px;background:rgba(255,255,255,.3);
  position:relative;overflow:hidden;}
#hero .scroll-ind .line i{position:absolute;top:-100%;left:0;width:100%;height:100%;
  background:#fff;animation:scrolldrop 2.2s ease-in-out infinite;}
@keyframes scrolldrop{0%{top:-100%;}50%{top:0;}100%{top:100%;}}
#hero .scroll-ind span{font-size:9px;letter-spacing:.25em;text-transform:uppercase;}
@media (prefers-reduced-motion: reduce){
  #hero .scroll-ind .line i{animation:none;}
}

/* Project detail headings and fact strip are centred. The hero caption,
   the story heading and the gallery label all share the page's centre line
   so the detail page reads as a single centred column. */
#projectDetail .pd-hero-caption{text-align:center;left:0;right:0;
  padding-left:24px;padding-right:24px;}
#projectDetail .pd-meta{justify-content:center;text-align:center;}
#projectDetail .g-head{justify-content:center;text-align:center;}
#projectDetail .g-tabs{justify-content:center;}

/* Not every project write-up supplies a tagline, so an empty lede
   collapses instead of leaving a blank line above the story. */
#projectDetail .pd-story-body .lede-split:empty{display:none;}

/* ---------- iOS / TOUCH ----------
   Safari-specific behaviours that make the page feel like a native app
   rather than a desktop site squeezed onto a phone. */

/* Grey flash on every tap, and the callout menu on a long press. */
html{-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:100%;}
a,button,.pcard,.filter-btn,.g-tab{-webkit-touch-callout:none;}

/* Safe-area padding so the fixed nav clears the notch and the footer
   clears the home indicator, now that viewport-fit=cover is set. */
@supports (padding:max(0px)){
  header#nav{padding-left:max(48px,env(safe-area-inset-left));
             padding-right:max(48px,env(safe-area-inset-right));
             padding-top:max(32px,env(safe-area-inset-top));}
  @media (max-width:1023px){
    header#nav{padding-left:max(24px,env(safe-area-inset-left));
               padding-right:max(24px,env(safe-area-inset-right));
               padding-top:max(24px,env(safe-area-inset-top));}
  }
  footer{padding-bottom:max(48px,env(safe-area-inset-bottom));}
}

/* iOS keeps :hover stuck on the last tapped element, so a card can stay
   lifted after the finger leaves. Neutralise hover transforms on touch. */
@media (hover:none),(pointer:coarse){
  .pcard:hover .frame,.pcard:hover img{transform:none!important;}
  .btn:hover,.filter-btn:hover{transform:none!important;}
  /* Inputs below 16px make Safari zoom the whole page on focus. */
  input,select,textarea{font-size:16px!important;}
}

/* ---------- MOBILE MENU ----------
   The Menu button existed in the markup from the start but had no handler
   and no panel, so tapping it did nothing. Below 1024px the desktop nav is
   display:none, which left the phone with only the logo.

   The panel is populated at runtime by cloning the real nav links, so the
   two menus can never drift apart. */
/* min-height/min-width:44px keeps the tap target at Apple/Google's minimum
   recommended size (44x44) even though the visible bars+label are smaller —
   on a real phone the old 6px-padding box measured well under that, which
   made the button easy to miss or require a second tap. justify-content
   centers the (unchanged) visible content inside the larger invisible box. */
header#nav .menu-btn{background:none;border:0;padding:6px 12px;cursor:pointer;
  display:none;align-items:center;justify-content:center;gap:10px;font-family:inherit;
  min-height:44px;min-width:44px;}
header#nav .menu-btn .mb-bars{display:inline-block;width:20px;height:12px;position:relative;}
header#nav .menu-btn .mb-bars i{position:absolute;left:0;width:100%;height:1px;
  background:currentColor;transition:transform .6s cubic-bezier(.65,0,.35,1),opacity .25s ease;}  /* ≈ power3.inOut, in step with the menu curtain */
header#nav .menu-btn .mb-bars i:nth-child(1){top:2px;}
header#nav .menu-btn .mb-bars i:nth-child(2){top:9px;}
/* Bars fold into a cross while the panel is open. */
header#nav .menu-btn[aria-expanded="true"] .mb-bars i:nth-child(1){transform:translateY(3.5px) rotate(45deg);}
header#nav .menu-btn[aria-expanded="true"] .mb-bars i:nth-child(2){transform:translateY(-3.5px) rotate(-45deg);}

/* The panel is a direct child of <body>, NOT of header#nav. It used to be
   nested in the header, and once the header picked up backdrop-filter
   (.scrolled / body.pd-locked) that made the header the containing block for
   fixed descendants — position:fixed;inset:0 then meant "the header's box",
   so the dark panel was ~90px tall and the links spilled out over the hero.

   Stacking: the panel sits at 9999, above every section, image, overlay page
   (#projectDetail 600) and the lightbox (9000). While it is open the header
   is lifted to 10001 (body.menu-open below) so the logo and Close button stay
   on top of it, exactly where they were.

   Layers, back to front:
     .mn-bg      the dark curtain (clip-path revealed by GSAP in app.js),
                 holding a faint hero still and the loader's 64px grid
     .mn-rule    hairline under the header row
     .mn-scroll  the scrolling layer with the links, so the curtain stays put
                 when the list is taller than the screen (320px / landscape)

   All motion lives in the GSAP timelines; nothing here transitions the
   properties GSAP animates, so the two never fight. display:none when closed
   so iOS Safari has nothing left to mis-paint. */
.mobile-nav{position:fixed;top:0;left:0;right:0;height:100vh;height:100dvh;z-index:9999;
  display:none;overflow:hidden;}
.mobile-nav.is-mounted{display:block;}
.mobile-nav .mn-bg{position:absolute;inset:0;background:#0b0b0b;overflow:hidden;
  clip-path:inset(0% 0% 100% 0%);will-change:clip-path;}
.mobile-nav .mn-media{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;filter:grayscale(.4) brightness(.6);transform-origin:50% 0;}
/* keeps the still a texture, never a picture competing with the type */
.mobile-nav .mn-bg::after{content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(11,11,11,.5) 0%,rgba(11,11,11,.86) 42%,#0b0b0b 100%);}
.mobile-nav .mn-grid{position:absolute;inset:0;z-index:1;opacity:0;pointer-events:none;
  background-image:linear-gradient(rgba(255,255,255,.035) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(255,255,255,.035) 1px,transparent 1px);
  background-size:64px 64px;
  -webkit-mask-image:linear-gradient(180deg,#000 0%,transparent 85%);mask-image:linear-gradient(180deg,#000 0%,transparent 85%);}
/* Same height as the header's rendered row: inset-aware top padding +
   44px tap target + 24px bottom padding. */
.mobile-nav .mn-rule{position:absolute;z-index:2;height:1px;background:rgba(255,255,255,.14);
  top:calc(max(24px, env(safe-area-inset-top, 0px)) + 44px + 24px);
  left:max(24px, env(safe-area-inset-left, 0px));right:max(24px, env(safe-area-inset-right, 0px));
  transform:scaleX(0);transform-origin:left center;}
/* overflow-y:auto + margin:auto on .mn-links (not justify-content:center)
   centres the list when it fits and scrolls from the real top when it
   doesn't, instead of clipping both ends. Top padding clears the header row
   plus breathing room, so the first link never meets the logo / Close. */
.mobile-nav .mn-scroll{position:absolute;inset:0;z-index:3;display:flex;flex-direction:column;
  overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;
  padding:calc(max(24px, env(safe-area-inset-top, 0px)) + 44px + 24px + 36px)
          max(24px, env(safe-area-inset-right, 0px))
          calc(40px + env(safe-area-inset-bottom, 0px))
          max(24px, env(safe-area-inset-left, 0px));}
.mobile-nav .mn-links{display:flex;flex-direction:column;align-items:flex-start;
  gap:clamp(4px,1.4vh,14px);width:100%;margin:auto 0;flex-shrink:0;}
.mobile-nav .mn-links a{display:block;width:100%;color:#fff;
  font-family:var(--font-serif);font-weight:400;
  font-size:clamp(32px,9.6vw,56px);line-height:1.08;letter-spacing:.005em;padding:4px 0;}
.mobile-nav .mn-row{position:relative;display:inline-flex;align-items:flex-start;gap:14px;
  transition:transform .6s cubic-bezier(.16,1,.3,1);}
.mobile-nav .mn-num{flex:none;min-width:2.4em;padding-top:10px;
  font-family:var(--font-sans);font-size:10px;letter-spacing:.22em;color:rgba(255,255,255,.42);}
.mobile-nav .mn-label{position:relative;display:block;}
/* the mask the words rise through; padding keeps the j/y descenders */
.mobile-nav .mn-mask{display:block;overflow:hidden;padding-bottom:.08em;}
.mobile-nav .mn-text{display:block;}
.mobile-nav .mn-label::after{content:'';position:absolute;left:0;right:0;bottom:.02em;height:1px;
  background:var(--accent-bright);transform:scaleX(0);transform-origin:left center;
  transition:transform .6s cubic-bezier(.16,1,.3,1);}
/* Hover / keyboard: an editorial nudge right and a drawn underline.
   Touch gets the nudge on press only, so nothing sticks after a tap. */
@media (hover:hover){
  .mobile-nav .mn-links a:hover .mn-row{transform:translateX(12px);}
  .mobile-nav .mn-links a:hover .mn-label::after{transform:scaleX(1);}
}
.mobile-nav .mn-links a:focus-visible .mn-row{transform:translateX(12px);}
.mobile-nav .mn-links a:focus-visible .mn-label::after{transform:scaleX(1);}
.mobile-nav .mn-links a:active .mn-row{transform:translateX(8px);}
.mobile-nav .mn-links a:active,
.mobile-nav .mn-links a.is-current{color:var(--accent-bright);}
.mobile-nav .mn-links a.is-current .mn-num{color:var(--accent);}
.mobile-nav .mn-links a.is-current .mn-label::after{transform:scaleX(1);opacity:.6;}
.mobile-nav .mn-links a:focus-visible{outline-color:var(--accent-bright);outline-offset:4px;}
@media (max-height:520px){
  .mobile-nav .mn-links a{font-size:clamp(24px,6.4vh,34px);padding:2px 0;}
  .mobile-nav .mn-num{padding-top:6px;}
}

/* Menu open: header (logo + Close) floats above the panel. Its scrolled
   glass treatment is dropped so it reads as part of the panel instead of
   a blurred bar over it. */
body.menu-open header#nav{z-index:10001;background:transparent;
  -webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:none;border-bottom-color:transparent;}

/* Only shown on the breakpoint where the desktop nav is hidden. */
@media (max-width:1023px){header#nav .menu-btn{display:inline-flex;}}
@media (min-width:1024px){.mobile-nav,.mobile-nav.is-mounted{display:none;}}
@media (prefers-reduced-motion: reduce){
  .mobile-nav .mn-row,.mobile-nav .mn-label::after{transition:none;}
  header#nav .menu-btn .mb-bars i{transition:none;}
}

/* ---------- SECTION HEAD ---------- */
.head{display:flex;justify-content:space-between;align-items:flex-end;gap:40px;margin-bottom:70px;flex-wrap:wrap;}
.head h2{font-size:clamp(34px,4.6vw,64px);line-height:1.05;max-width:640px;}
.head p{max-width:340px;color:var(--text-2);font-size:14.5px;line-height:1.8;font-weight:300;}
/* Sits as the first pill in the filter row, styled identically to the
   category filters — just with an accent border so it still reads as
   an action rather than another category. */
.browse-all-filter-btn{border-color:var(--accent);color:var(--text);}
.browse-all-filter-btn:hover{background:var(--accent);color:#fff;border-color:var(--accent);}


/* ---------- ABOUT ---------- */
#about{padding:180px 0;background:var(--bg-2);border-top:1px solid var(--border);border-bottom:1px solid var(--border);}
#about .grid{display:grid;grid-template-columns:1fr 1fr;gap:100px;align-items:center;}
#about .imgwrap{position:relative;aspect-ratio:4/5;overflow:hidden;clip-path:inset(0% 0% 0% 0%);
  opacity:0;transform:translateX(-90px);transition:opacity 1s cubic-bezier(.22,1,.36,1),transform 1s cubic-bezier(.22,1,.36,1);}
/* Plain CSS-transition reveal, deliberately NOT GSAP-driven — this
   exact photo doubles as the source for the full About page's opening
   transition (see openAboutPage()), which has to gsap.killTweensOf()
   it to hand it off. A GSAP tween can be caught mid-flight by that and
   frozen hidden; a CSS class toggle can't — .is-revealed is only ever
   added, never removed, by the IntersectionObserver in initReveals(). */
#about .imgwrap.is-revealed{opacity:1;transform:translateX(0);}
/* Reveals by fading and un-blurring rather than wiping with clip-path.
   The clip left the lower part of the frame empty mid-transition, and with
   the dark ::after gradient sitting over the white background that read as
   a solid grey band across the photo. */
#about .imgwrap img{width:100%;height:100%;object-fit:cover;transform:scale(1.14);will-change:transform,opacity;
  opacity:0;filter:blur(14px);transition:opacity 1.1s cubic-bezier(.22,1,.36,1),filter 1.15s ease;}
#about .imgwrap.is-revealed img{opacity:1;filter:blur(0px);}
#about .num{position:absolute;bottom:24px;left:24px;color:#fff;font-family:var(--font-serif);font-size:13px;letter-spacing:.2em;z-index:2;}
/* Gradient scrim removed — it was the grey wash over the photo. The caption
   keeps its legibility from a shadow instead of a full-width overlay. */
#about .num{text-shadow:0 1px 14px rgba(0,0,0,.75),0 0 4px rgba(0,0,0,.5);}
#about .copy .eyebrow{margin-bottom:26px;}
#about h2{font-size:clamp(30px,3.4vw,46px);line-height:1.3;font-weight:400;margin-bottom:34px;}
#about h2 em{font-style:italic;color:var(--accent);}
#about .lines p{overflow:hidden;margin-bottom:6px;}
#about .lines p span{display:inline-block;font-size:15px;line-height:1.9;color:var(--text-2);font-weight:300;font-family:var(--font-sans);}
#about .stats{display:flex;flex-wrap:wrap;gap:40px 56px;margin-top:52px;}
#about .stats div h3{font-size:38px;color:var(--text);font-weight:400;}
#about .stats div span{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--text-2);}
@media (max-width:1023px){#about .grid{grid-template-columns:1fr;gap:56px;}#about{padding:120px 0;}}

/* ---------- PROJECTS ----------
   Redesigned as a framed, gallery-style grid: each photo sits inside a
   thin bordered "mat" (like a printed, matted photograph) with generous
   gutters between cards, rather than the previous edge-to-edge mosaic
   with a heavy dark scrim. Caption (index, category, title, location)
   lives below the frame instead of overlaid on the image, so it reads
   the same way the Journal cards already do elsewhere on the page —
   one consistent card language across sections. The photos themselves,
   their crops and aspect ratios are unchanged. */
#projects{padding:180px 0;}
.filter-bar{display:flex;flex-wrap:wrap;gap:12px;margin-top:34px;}
/* Projects heading and its filter row stack instead of sitting on one
   line. .head is a shared flex row used by several sections, so this
   is scoped to #projects and leaves the others alone. */
#projects .head{flex-direction:column;align-items:flex-start;gap:0;}
/* Heading reads "Projects", not "PROJECTS" — the uppercase transform was
   removed at the client's request. The markup was always title case. */
#projects .head h2{letter-spacing:.01em;}
#projects .head .filter-bar{width:100%;margin-top:26px;}
.filter-btn{font-family:var(--font-sans);font-size:11px;letter-spacing:.18em;text-transform:uppercase;font-weight:500;color:var(--text-2);background:transparent;border:1px solid var(--border);padding:11px 22px;border-radius:999px;cursor:pointer;transition:color .35s ease,border-color .35s ease,background .35s ease;}
.filter-btn:hover{color:var(--text);border-color:var(--accent);}
.filter-btn.active{color:#fff;background:var(--dark);border-color:var(--dark);}
.filter-empty{display:none;text-align:center;padding:80px 24px;color:var(--text-2);font-size:14px;letter-spacing:.02em;}
.filter-empty.show{display:block;}
@media (max-width:640px){.filter-bar{gap:8px;}.filter-btn{padding:9px 16px;font-size:10px;}}
/* "Browse All Works" — a premium text CTA below the grid, in the same
   uppercase/tracked-out language as the filter pills and eyebrows.
   Hover motion uses only transform (underline scaleX + arrow translateX)
   so it stays GPU-accelerated. */
.browse-all-wrap{margin-top:72px;text-align:center;}
.browse-all-cta{position:relative;display:inline-flex;align-items:center;gap:9px;font-family:var(--font-sans);font-size:12px;letter-spacing:.2em;text-transform:uppercase;font-weight:500;color:var(--text);text-decoration:none;padding-bottom:8px;cursor:pointer;background:none;border:none;}
.browse-all-cta::after{content:'';position:absolute;left:0;bottom:0;width:100%;height:1px;background:var(--accent);transform:scaleX(0);transform-origin:left;transition:transform .55s cubic-bezier(.16,1,.3,1);will-change:transform;}
.browse-all-cta:hover::after{transform:scaleX(1);}
.browse-all-cta .arrow{display:inline-block;transition:transform .55s cubic-bezier(.16,1,.3,1);will-change:transform;}
.browse-all-cta:hover .arrow{transform:translateX(7px);}
/* Grid rhythm: align-items:start (not stretch) is deliberate — with
   stretch, a taller sibling caption (longer title/location text) would
   force every card in that row to match its height, and since .imgbox
   below had both height:100% AND aspect-ratio:4/5 set, the 100% height
   won and silently broke the 4:5 ratio per-card, so images ended up
   visibly different sizes/proportions row to row. Each card now sizes
   to its own natural content, and its image is guaranteed the exact
   same 4:5 frame every time, independent of what its neighbors' text
   happens to do. Gap is a single equal value in both directions for a
   consistent, even rhythm rather than tighter columns than rows. */
.masonry{display:grid;grid-template-columns:repeat(3,1fr);gap:48px;align-items:start;}
/* With just one card, keep it at the same size a single grid column
   would give it rather than stretching it full-width — same frame,
   border, caption and hover treatment, just one photo instead of a grid. */
.masonry:has(.pcard:only-child){grid-template-columns:minmax(0,440px);justify-content:center;}
.masonry:has(.pcard:only-child) .pcard:nth-child(1){grid-row:auto;}
.masonry:has(.pcard:only-child) .pcard .imgbox{aspect-ratio:4/5!important;height:auto!important;}
.pcard{position:relative;cursor:pointer;display:flex;flex-direction:column;}

/* padding:0 — the photo now sits flush against the border, with no white
   band between them. border-radius must always equal (padding + the
   image's own 20px radius), so with zero padding it drops to 20px and
   the border traces the photo's rounded edge exactly. If they drift
   apart, the corners flare into white wedges even when the straight
   edges look right. */
.pcard .frame{position:relative;overflow:hidden;background:var(--bg-2);border:1px solid var(--border);border-radius:20px;padding:0;transition:border-color .6s ease,box-shadow .6s ease,transform .5s cubic-bezier(.16,1,.3,1);flex:0 0 auto;transform-style:preserve-3d;will-change:transform;}
.pcard:hover .frame{border-color:var(--accent);box-shadow:0 34px 64px rgba(0,0,0,.14);}
.pcard .spot{position:absolute;inset:0;z-index:3;pointer-events:none;opacity:0;transition:opacity .5s ease;border-radius:20px;
  background:radial-gradient(240px circle at var(--mx,50%) var(--my,50%), rgba(255,255,255,.55), transparent 60%);
  mix-blend-mode:overlay;}
.pcard:hover .spot{opacity:1;}
@media (hover:none),(pointer:coarse){.pcard .frame{transform:none!important;}.pcard .spot{display:none;}}

.pcard .imgbox{position:relative;width:100%;overflow:hidden;border-radius:20px;perspective:1000px;transform-style:preserve-3d;transition:transform .5s cubic-bezier(.16,1,.3,1);aspect-ratio:4/5;}

/* The resting transform used to be scale(1.13), which zoomed 13% into
   every photo and permanently cropped ~6.5% off all four edges before
   the visitor had even hovered — and because hover went DOWN to 1.02,
   the default state was the most cropped one of the two. Resting at
   scale(1) now shows the whole 4:5 frame, and hover zooms gently
   upward (1 → 1.045) the way a hover zoom normally reads. object-fit
   stays 'cover' with a centred origin so every card fills its frame
   edge-to-edge at an identical size — 'contain' would letterbox
   non-4:5 source photos and break the uniform grid. */
.pcard img{width:100%;height:100%;object-fit:cover;object-position:center;border-radius:20px;transform:scale(1);transition:transform 1.1s cubic-bezier(.16,1,.3,1),filter .6s ease;}
.pcard:hover img{transform:scale(1.045);filter:brightness(1.05);}

/* Hover affordance replaces the old full-image dark scrim — a single
   small "view" mark that appears over the corner of the frame instead
   of dimming the whole photograph. */
.pcard .peek{position:absolute;top:22px;right:22px;width:42px;height:42px;border-radius:50%;background:rgba(248,247,244,.92);color:var(--dark);display:flex;align-items:center;justify-content:center;font-size:15px;line-height:1;opacity:0;transform:translate(-6px,6px) scale(.82);transition:opacity .5s cubic-bezier(.16,1,.3,1),transform .5s cubic-bezier(.16,1,.3,1);z-index:2;pointer-events:none;}
.pcard:hover .peek{opacity:1;transform:translate(0,0) scale(1);}

/* Every card's image is already an identical 4:5 box, so the ONLY thing
   that could make one card taller than its neighbour is this caption —
   and with align-items:start, a taller card in a row leaves a visibly
   bigger gap under every shorter card beside it. Clamping the title to
   exactly two lines and the location to one makes the caption a fixed,
   predictable height, so all cards match and the 48px grid gap is the
   real spacing everywhere instead of a minimum. */
.pcard .caption{flex:0 0 auto;display:flex;align-items:baseline;gap:0;padding:18px 2px 0;min-height:auto;}
.pcard .caption .idx{flex:0 0 auto;font-family:var(--font-serif);font-size:14px;color:var(--text-2);letter-spacing:.04em;padding-top:1px;}
.pcard .caption .meta{flex:1;min-width:0;}
.pcard .caption .cat{display:block;font-size:10px;letter-spacing:.22em;text-transform:uppercase;color:var(--accent);font-weight:500;margin-bottom:9px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.pcard /* Card caption is just the project name now — the index number, category
   label and location were removed, so this is small caps on one line. */
/* Scales with the viewport so the name stays in proportion to the card
   rather than looking undersized on a wide screen. */
.caption h3{font-size:clamp(12px,1.05vw,17px);line-height:1.35;font-weight:400;
  letter-spacing:.14em;text-transform:uppercase;margin-bottom:0;
  transition:color .4s ease;display:-webkit-box;-webkit-box-orient:vertical;
  -webkit-line-clamp:2;overflow:hidden;}
.pcard:hover .caption h3{color:var(--accent);}
.pcard .caption .loc{display:block;font-size:12px;color:var(--text-2);font-weight:300;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

/* ---------- MARQUEE ---------- */

@media (max-width:1023px){.masonry{grid-template-columns:repeat(2,1fr);gap:44px;}}
@media (max-width:640px){.masonry{grid-template-columns:1fr;gap:40px;}}

/* ---------- PHILOSOPHY ---------- */

/* ---------- SERVICES ---------- */
#services{padding:180px 0;background:var(--bg-2);border-top:1px solid var(--border);border-bottom:1px solid var(--border);}
.svc-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--border);border:1px solid var(--border);}
.svc{background:var(--bg-2);padding:46px 34px;transition:transform .5s cubic-bezier(.16,1,.3,1),box-shadow .5s ease,background .5s ease,border-color .5s ease;position:relative;}
.svc .n{font-size:11px;color:var(--text-2);letter-spacing:.18em;margin-bottom:44px;display:block;}
.svc h3{font-size:24px;font-weight:400;margin-bottom:12px;}
.svc p{font-size:13px;color:var(--text-2);line-height:1.75;font-weight:300;}
.svc:hover{transform:translateY(-8px);box-shadow:0 34px 70px rgba(0,0,0,.10),0 0 0 1px rgba(176,139,87,.12);background:var(--glass-bg);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-bottom:2px solid var(--accent);}
@media (max-width:1023px){.svc-grid{grid-template-columns:repeat(2,1fr);}}
@media (max-width:640px){.svc-grid{grid-template-columns:1fr;}}

/* ---------- PROCESS ---------- */
#process{padding:180px 0;background:var(--dark);color:#fff;}
#process .head h2,#process .head p{color:#fff;}
#process .head p{color:rgba(255,255,255,.55);}
.timeline{position:relative;}
.tline-track{position:relative;display:flex;justify-content:space-between;padding-top:10px;}
.tline-bar{position:absolute;top:0;left:0;width:100%;height:1px;background:rgba(255,255,255,.14);}
.tline-bar .fill{position:absolute;top:0;left:0;height:1px;width:100%;background:var(--accent);transform:scaleX(var(--fill,0));transform-origin:0 0;will-change:transform;}
.tstep{position:relative;flex:1;padding-top:34px;padding-right:20px;}
.tstep .dot{position:absolute;top:-4px;left:0;width:9px;height:9px;border-radius:50%;background:rgba(255,255,255,.25);transition:background .4s ease,box-shadow .4s ease;}
.tstep.active .dot{background:var(--accent);box-shadow:0 0 0 5px rgba(176,139,87,.18);}
.tstep .n{font-size:11px;color:rgba(255,255,255,.4);letter-spacing:.18em;display:block;margin-bottom:12px;}
.tstep h4{font-size:19px;font-weight:400;margin-bottom:8px;}
.tstep p{font-size:12.5px;color:rgba(255,255,255,.5);line-height:1.7;font-weight:300;}
@media (max-width:1023px){.tline-track{flex-direction:column;gap:36px;}.tline-bar{width:1px;height:100%;left:0;top:0;}.tline-bar .fill{width:100%;height:100%;transform:scaleY(var(--fill,0));}.tstep{padding-top:0;padding-left:34px;padding-right:0;}.tstep .dot{top:6px;left:-4px;}}

/* ---------- TESTIMONIALS ---------- */
#testimonials{padding:180px 0;}
/* Four client notes: two columns fill exactly two rows. At three columns
   the fourth card sat alone beside a large empty cell. */
.t-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--border);border:1px solid var(--border);}
.tcard{background:var(--bg);padding:48px 38px;display:flex;flex-direction:column;gap:30px;}
.tcard .quote{font-family:var(--font-serif);font-style:italic;font-size:21px;line-height:1.55;font-weight:400;}
.tcard .who{display:flex;align-items:center;gap:14px;margin-top:auto;}
.tcard .who img{width:44px;height:44px;border-radius:50%;object-fit:cover;filter:grayscale(1);}
.tcard .who h5{font-size:13px;font-weight:500;font-family:var(--font-sans);}
.tcard .who span{font-size:11px;color:var(--text-2);}
@media (max-width:1023px){.t-grid{grid-template-columns:1fr;}}


/* ---------- CONTACT ---------- */
#contact{padding:180px 0;}
.c-grid{display:grid;grid-template-columns:1fr 1fr;gap:100px;}
.c-img{position:relative;aspect-ratio:4/5;overflow:hidden;}
.c-img img{width:100%;height:100%;object-fit:cover;}
.c-form .eyebrow{margin-bottom:24px;}
.c-form h2{font-size:clamp(30px,3.4vw,48px);font-weight:400;margin-bottom:44px;max-width:480px;}
.field{position:relative;border-bottom:1px solid var(--border);padding:16px 0;margin-bottom:6px;}
.field input,.field textarea{width:100%;background:transparent;border:none;outline:none;font-family:var(--font-sans);font-weight:300;font-size:15px;color:var(--text);resize:none;}
.field label{position:absolute;top:16px;left:0;font-size:14px;color:var(--text-2);pointer-events:none;transition:all .3s ease;font-weight:300;}
.field input:focus ~ label,.field input:not(:placeholder-shown) ~ label,.field textarea:focus ~ label,.field textarea:not(:placeholder-shown) ~ label{top:-10px;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent);}
.field.line{border-bottom-color:var(--border);}
.field::after{content:'';position:absolute;bottom:-1px;left:0;width:0;height:1px;background:var(--accent);transition:width .5s cubic-bezier(.16,1,.3,1);}
.field:focus-within::after{width:100%;}
.c-form .submit{margin-top:34px;}
/* Reserves its own height even when empty, so the button and everything
   below it don't jump the moment a status message appears. */
.form-status{margin-top:18px;font-size:13px;font-weight:300;line-height:1.5;color:var(--text-2);min-height:20px;}
.form-status.ok{color:var(--accent);}
.form-status.err{color:#b4443a;}
.c-form .submit[disabled]{opacity:.55;cursor:default;}
@media (max-width:1023px){.c-grid{grid-template-columns:1fr;gap:56px;}.c-img{order:2;aspect-ratio:16/9;}}

/* ---------- BUTTONS ---------- */
.btn{display:inline-flex;align-items:center;gap:14px;font-size:11px;letter-spacing:.2em;text-transform:uppercase;padding:18px 34px;border:1px solid var(--dark);color:var(--dark);position:relative;overflow:hidden;background:transparent;cursor:pointer;transition:box-shadow .4s ease;font-family:var(--font-sans);}
.btn::before{content:'';position:absolute;inset:0;background:var(--dark);transform:translateY(101%);transition:transform .5s cubic-bezier(.16,1,.3,1);z-index:0;}
.btn span{position:relative;z-index:1;transition:color .4s ease;}
.btn:hover::before{transform:translateY(0);}
.btn:hover span{color:#fff;}
.btn:hover{box-shadow:0 12px 28px rgba(176,139,87,.28);}
.btn.light{border-color:rgba(255,255,255,.4);color:#fff;}
.btn.light::before{background:var(--accent);}
.magnetic{will-change:transform;}

/* ---------- FOOTER ---------- */
footer{background:var(--dark);color:#fff;padding:140px 0 40px;}
footer .top{display:flex;justify-content:space-between;align-items:flex-end;flex-wrap:wrap;gap:60px;margin-bottom:110px;}
footer .biglogo{font-family:var(--font-serif);font-size:clamp(48px,9vw,100px);line-height:.9;letter-spacing:.01em;
  background-image:linear-gradient(100deg, rgba(255,255,255,.92) 0%, var(--accent-hover) 45%, rgba(255,255,255,.92) 65%);
  background-size:220% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;
  animation:footerSweep 9s ease-in-out infinite;}
@keyframes footerSweep{0%{background-position:180% 0;}100%{background-position:-60% 0;}}
@media (prefers-reduced-motion: reduce){footer .biglogo{animation:none;background:none;-webkit-text-fill-color:currentColor;color:#fff;}}
footer .cols{display:flex;gap:90px;flex-wrap:wrap;}
footer .col h5{font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:rgba(255,255,255,.45);margin-bottom:22px;}
footer .col a{display:block;font-size:14px;color:rgba(255,255,255,.85);margin-bottom:12px;font-weight:300;}
footer .bottom{display:flex;justify-content:space-between;padding-top:34px;border-top:1px solid rgba(255,255,255,.1);font-size:11px;color:rgba(255,255,255,.4);flex-wrap:wrap;gap:16px;}
footer .social{display:flex;gap:26px;}
/* Social links: mark above, platform name below. Laid out as a row of
   small columns so the labels sit centred under their own icon. */
footer .social.social-icons{gap:22px;flex-wrap:wrap;}
footer .social.social-icons a{display:flex;flex-direction:column;align-items:center;gap:8px;
  margin-bottom:0;min-width:64px;text-align:center;color:rgba(255,255,255,.85);
  transition:color .3s ease,transform .3s ease;}
footer .social.social-icons a .si-ico{width:38px;height:38px;border-radius:50%;
  border:1px solid rgba(255,255,255,.18);display:flex;align-items:center;justify-content:center;
  transition:border-color .3s ease,background .3s ease;}
footer .social.social-icons a svg{width:17px;height:17px;display:block;
  fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;}
footer .social.social-icons a .si-name{font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  color:rgba(255,255,255,.5);transition:color .3s ease;}
footer .social.social-icons a:hover{color:var(--accent);transform:translateY(-2px);}
footer .social.social-icons a:hover .si-ico{border-color:var(--accent);background:rgba(255,255,255,.05);}
footer .social.social-icons a:hover .si-name{color:var(--accent);}
footer .social.social-icons a:focus-visible .si-ico{border-color:var(--accent);}
@media (max-width:480px){footer .social.social-icons{gap:14px;}
  footer .social.social-icons a{min-width:0;}
  footer .social.social-icons a .si-name{letter-spacing:.1em;font-size:9.5px;}}

/* ---------- GENERAL REVEAL ---------- */
.reveal{opacity:0;transform:translateY(50px);}

/* ========================================================
   PROJECT DETAIL — cinematic Flip transition overlay.
   Its own fixed-position, self-scrolling layer (own Lenis
   instance) so it never disturbs the main page's scroll
   position — closing just un-hides #mainContent exactly
   where the user left it. Fully themed on the site's own
   tokens/fonts; no Perimeter colors, fonts or grayscale
   filters carried over — only the interaction pattern.
   ======================================================== */
body.pd-locked{overflow:hidden;}

/* No will-change:transform here. A transform (or a promise of one) makes
   this element the containing block for its position:fixed children,
   and because it is also the scroll container, that turns the fixed
   back button into an absolutely-positioned one that scrolls away with
   the content. will-change:opacity is all this actually needs — opacity
   is the only property animated on it in the steady state. */
#projectDetail{position:fixed;inset:0;z-index:600;background:var(--bg);display:none;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;will-change:opacity;}
#projectDetail.is-open{display:block;}

.pd-hero{position:relative;width:100%;height:100vh;height:100dvh;overflow:hidden;background:#050505;}
.pd-hero-media{position:absolute;top:-2%;left:-2%;width:104%;height:104%;}
.pd-hero-media img{width:100%;height:100%;object-fit:cover;display:block;}
.pd-hero-overlay{position:absolute;inset:0;background:#000;opacity:0;z-index:2;pointer-events:none;}
.pd-back{position:absolute;top:32px;left:48px;z-index:20;display:flex;align-items:center;gap:12px;color:#fff;font-size:11px;letter-spacing:.22em;text-transform:uppercase;opacity:0;cursor:pointer;font-family:var(--font-sans);background:none;border:none;}
/* Sticky variant, used on project pages so the way out stays reachable
   from anywhere in a long project. This only works while no ancestor
   establishes a containing block for fixed positioning — see the note on
   #projectDetail: any transform, will-change:transform, filter or
   clip-path on it would silently demote this to absolute positioning and
   the button would scroll off the top. It also lives OUTSIDE .pd-hero,
   which is overflow:hidden and would otherwise clip it away once the
   hero scrolls past. */
.pd-back.pd-back-fixed{position:fixed;top:32px;left:48px;z-index:50;padding:0;border-radius:999px;border:1px solid transparent;transition:background .35s ease,padding .35s ease,border-color .35s ease,backdrop-filter .35s ease;}
/* Over the hero the button sits on a dark photo, so plain white text is
   correct. Past the hero the page turns cream (--bg) and white-on-white
   would vanish — so it lifts into a dark glass pill instead, which stays
   legible over both the light page and any gallery image behind it. */
.pd-back.pd-back-fixed.is-lifted{background:rgba(13,13,13,.72);-webkit-backdrop-filter:blur(10px) saturate(140%);backdrop-filter:blur(10px) saturate(140%);border-color:rgba(255,255,255,.14);padding:12px 22px;}
.pd-back .pd-back-line{width:30px;height:1px;background:#fff;}
.pd-back:hover .pd-back-line{background:var(--accent);}
@media (max-width:768px){.pd-back{left:24px;top:24px;}.pd-back.pd-back-fixed{left:24px;top:24px;}.pd-back.pd-back-fixed.is-lifted{padding:10px 18px;}}
/* The nav no longer disappears behind overlay pages, so it occupies the
   top-left corner the Back control used to have to itself. Drop Back
   below the bar so the two never overlap. */
body.pd-locked .pd-back,
body.pd-locked .pd-back.pd-back-fixed{top:108px;}
@media (max-width:768px){
  body.pd-locked .pd-back,
  body.pd-locked .pd-back.pd-back-fixed{top:92px;}
}

.pd-hero-caption{position:absolute;left:0;right:0;bottom:0;z-index:3;padding:0 48px 70px;color:#fff;}
.pd-hero-caption .cat{display:block;overflow:hidden;font-size:11px;letter-spacing:.3em;text-transform:uppercase;color:var(--accent);margin-bottom:22px;}
.pd-hero-caption .cat span{display:block;transform:translateY(100%);}
/* Long titles ran past the viewport at 140px. Capped, and the caption is
   allowed to wrap rather than clip. */
.pd-hero-caption h2{font-family:var(--font-serif);font-weight:400;
  font-size:clamp(34px,5.4vw,84px);line-height:1.02;margin-bottom:20px;
  max-width:min(1100px,92vw);margin-left:auto;margin-right:auto;
  /* .inner starts at translateY(100%) and uses this as its reveal mask —
     removing it would leave the title visible before it animates. Extra
     bottom padding keeps descenders from being clipped now that long
     titles wrap onto a second line. */
  overflow:hidden;padding-bottom:.12em;}
.pd-hero-caption h2 .inner{display:block;transform:translateY(100%);}
.pd-hero-caption .loc{display:block;overflow:hidden;font-size:13px;letter-spacing:.05em;color:rgba(255,255,255,.8);}
.pd-hero-caption .loc span{display:block;transform:translateY(100%);}
@media (max-width:768px){.pd-hero-caption{padding:0 24px 50px;}}

.pd-meta{display:grid;grid-template-columns:repeat(4,1fr);border-bottom:1px solid var(--border);padding:26px 48px;background:var(--bg-2);}
.pd-meta > div .label{display:block;font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--text-2);margin-bottom:8px;}
.pd-meta > div .val{font-family:var(--font-serif);font-size:17px;color:var(--text);}
@media (max-width:1023px){.pd-meta{grid-template-columns:repeat(3,1fr);gap:22px;padding:26px 24px;}}
@media (max-width:600px){.pd-meta{grid-template-columns:repeat(2,1fr);}}

.pd-story{display:grid;grid-template-columns:.7fr 1.3fr;gap:90px;padding:150px 48px;border-bottom:1px solid var(--border);}
.pd-story-sticky{position:sticky;top:140px;align-self:start;}
.pd-story-sticky .kicker{display:block;font-size:11px;letter-spacing:.24em;text-transform:uppercase;color:var(--accent);margin-bottom:18px;}
.pd-story-sticky h3{font-family:var(--font-serif);font-size:46px;font-weight:400;}
.pd-story-body p{font-size:16.5px;line-height:1.95;color:var(--text-2);font-weight:300;max-width:640px;margin-bottom:30px;}
.pd-story-body p:first-child{font-family:var(--font-serif);font-style:italic;font-size:24px;color:var(--text);line-height:1.5;font-weight:400;}
@media (max-width:1023px){.pd-story{grid-template-columns:1fr;gap:40px;padding:100px 24px;}.pd-story-sticky{position:relative;top:0;}}

.pd-stats{display:grid;grid-template-columns:repeat(4,1fr);border-bottom:1px solid var(--border);}
.pd-stat{padding:56px 34px;border-left:1px solid var(--border);}
.pd-stat:first-child{border-left:none;}
.pd-stat .n{font-family:var(--font-serif);font-size:46px;font-weight:400;color:var(--accent);}
.pd-stat .l{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--text-2);margin-top:8px;}
@media (max-width:1023px){.pd-stats{grid-template-columns:repeat(2,1fr);}.pd-stat{padding:40px 24px;}}

.pd-gallery{padding:150px 48px;border-bottom:1px solid var(--border);position:relative;}
.pd-gallery .g-head{display:flex;align-items:baseline;margin-bottom:56px;}
.g-tabs{display:flex;align-items:baseline;gap:28px;}
.g-tab{font:inherit;background:none;border:none;padding:0;cursor:pointer;color:var(--text-2);opacity:.5;transition:color .35s ease,opacity .35s ease;}
.g-tab:hover{opacity:.8;}
.g-tab.active{color:var(--accent);opacity:1;}
.pd-gallery-panels{position:relative;}
/* grid-auto-flow:dense so the 2-up/3-up rows always pack full. Without it,
   a run like portrait-portrait-landscape leaves the landscape (span 3)
   unable to fit the 2 remaining columns, so it drops to the next row and
   strands a visible hole. Dense pulls a later same-size figure up into
   that hole instead, which is what keeps every row reading as a clean
   2-landscape or 3-portrait band. */
/* ---------- GALLERY GRID ----------
   A single uniform grid: every tile is the same size, every row lines up,
   and the last row keeps the same rhythm as the rest.

   This replaces a justified-rows flex layout that was sizing each tile from
   its own photo. Rows were left ragged, and a final row holding one or two
   photos left a large hole beside them. Worse, several CSS-grid rules
   (grid-column:span, grid-template-columns) had been left behind and did
   nothing inside a flex container, so the responsive breakpoints silently
   stopped working.

   Uniform tiles mean each photo is cropped to fit rather than letterboxed.
   4:3 is the compromise: it trims a little from the 3:2 and 16:9 landscapes
   and takes more from the portraits, but nothing is distorted, and the grid
   reads as one clean block. The ratio is fixed, so tiles reserve their
   height before the photos load and the page never jumps. */
.pd-gallery-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:22px;}
.pd-gallery-grid figure{overflow:hidden;position:relative;margin:0;}
.pd-gallery-grid .pd-gmask{display:block;overflow:hidden;background:transparent;
  aspect-ratio:4/3;}
.pd-gallery-grid img{width:100%;height:100%;object-fit:cover;object-position:center;
  display:block;transition:transform 1.2s cubic-bezier(.16,1,.3,1);}
.pd-gallery-grid figure:hover img{transform:scale(1.03);}

@media (max-width:1023px){
  .pd-gallery{padding:100px 24px;}
  .pd-gallery-grid{gap:18px;}
}
@media (max-width:640px){
  .pd-gallery-grid{grid-template-columns:1fr;gap:14px;}
}

/* Vertical rail label running alongside the gallery, echoing the small
   tracked-out uppercase language used for eyebrows elsewhere on the
   site — a quiet section marker rather than a functional control. */
.pd-gallery-vlabel{position:absolute;top:150px;left:14px;writing-mode:vertical-rl;transform:rotate(180deg);font-family:var(--font-sans);font-size:11px;letter-spacing:.32em;text-transform:uppercase;color:var(--text-2);opacity:.6;pointer-events:none;display:none;}
@media (min-width:1200px){.pd-gallery-vlabel{display:block;}}

/* Portrait drawing gallery — same card proportions (4:5), same grid
   gap and reveal language as the photo gallery it shares a tab-slot
   with, just swapped in as the active panel. */
.pd-drawing-photos-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:22px;}
.pd-drawing-photos-grid figure{overflow:hidden;}
.pd-dmask{overflow:hidden;aspect-ratio:4/5;background:var(--bg-2);display:flex;align-items:center;justify-content:center;}
.pd-drawing-photos-grid img{width:100%;height:100%;object-fit:contain;object-position:center;display:block;transition:transform 1.2s cubic-bezier(.16,1,.3,1);}
.pd-drawing-photos-grid figure:hover img{transform:scale(1);}
@media (max-width:1023px){.pd-drawing-photos-grid{grid-template-columns:repeat(2,1fr);}}
@media (max-width:640px){.pd-drawing-photos-grid{grid-template-columns:1fr;}}

/* ---------- DESIGN STORY — line-mask reveal on the lede paragraph ---------- */
.pd-story-body p.lede-split .line{overflow:hidden;display:block;}

/* ---------- ALL WORKS PAGE ----------
   Reuses .pd-hero / .pd-hero-caption (title styling, scroll parallax)
   from the project-detail template, and .masonry/.pcard/.filter-bar
   verbatim from the homepage grid — only the intro paragraph and shell
   spacing below are new. */
.pd-works-shell{padding:80px 0 160px;}

/* ---------- CAREERS PAGE ----------
   Reuses the shared overlay chrome (.pd-hero, .pd-back, .pd-reveal) so the
   entrance, sticky back button and scroll reveals behave exactly like the
   About and Works pages. Only the body content below the hero is new. */
.pd-careers-shell{padding:96px 0 40px;}
.cr-lead{font-family:var(--font-serif);font-size:clamp(24px,3.2vw,38px);line-height:1.45;
  color:var(--text);max-width:820px;margin:0 0 26px;font-weight:400;}
.cr-lead em{font-style:italic;color:var(--accent);}
.cr-sub{font-size:16px;line-height:1.95;color:var(--text-2);font-weight:300;max-width:640px;margin:0;}

.cr-section{padding:96px 0;}
.cr-section + .cr-section{border-top:1px solid var(--border);}
.cr-section h3{font-family:var(--font-serif);font-size:clamp(24px,3.4vw,40px);
  font-weight:400;margin:16px 0 54px;max-width:640px;}
.cr-section h3 em{font-style:italic;color:var(--accent);}

/* why join us */
.cr-values{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--border);
  border:1px solid var(--border);}
.cr-vcard{background:var(--bg-2);padding:44px 32px;}
.cr-vcard .n{font-family:var(--font-serif);font-size:26px;color:var(--accent);display:block;margin-bottom:14px;}
.cr-vcard h4{font-size:18px;font-weight:400;margin-bottom:10px;}
.cr-vcard p{font-size:13.5px;line-height:1.8;color:var(--text-2);font-weight:300;}
@media (max-width:900px){.cr-values{grid-template-columns:1fr;}}

/* open roles — a list of expandable rows rather than cards, so long
   descriptions never force every row to the height of the longest one */
.cr-role{border-bottom:1px solid var(--border);}
.cr-role:first-child{border-top:1px solid var(--border);}
.cr-role-head{width:100%;background:none;border:0;padding:32px 4px;display:grid;
  grid-template-columns:1fr auto auto;align-items:center;gap:28px;text-align:left;
  cursor:pointer;color:inherit;font:inherit;transition:padding-left .45s cubic-bezier(.16,1,.3,1);}
.cr-role-head:hover{padding-left:14px;}
.cr-role-title{font-family:var(--font-serif);font-size:clamp(20px,2.4vw,27px);font-weight:400;}
.cr-role-meta{font-size:10.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--text-2);}
.cr-role-toggle{width:30px;height:30px;border:1px solid var(--border);border-radius:50%;
  position:relative;flex:none;transition:background .4s ease,border-color .4s ease,transform .5s cubic-bezier(.16,1,.3,1);}
.cr-role-toggle::before,.cr-role-toggle::after{content:'';position:absolute;top:50%;left:50%;
  width:11px;height:1px;background:var(--text);transform:translate(-50%,-50%);transition:opacity .35s ease,background .35s ease;}
.cr-role-toggle::after{transform:translate(-50%,-50%) rotate(90deg);}
.cr-role.is-open .cr-role-toggle{background:var(--accent);border-color:var(--accent);transform:rotate(90deg);}
.cr-role.is-open .cr-role-toggle::after{opacity:0;}
.cr-role.is-open .cr-role-toggle::before{background:#fff;}
/* height is animated from 0 to the measured content height in JS, so the
   row can expand smoothly without a hardcoded max-height guess */
.cr-role-body{height:0;overflow:hidden;transition:height .6s cubic-bezier(.16,1,.3,1);}
.cr-role-body-inner{padding:0 4px 36px;max-width:720px;}
.cr-role-body p{font-size:14px;line-height:1.9;color:var(--text-2);font-weight:300;margin-bottom:18px;}
.cr-role-body ul{list-style:none;margin:0 0 22px;padding:0;}
.cr-role-body li{font-size:13.5px;line-height:1.85;color:var(--text-2);font-weight:300;
  padding-left:20px;position:relative;margin-bottom:7px;}
.cr-role-body li::before{content:'';position:absolute;left:0;top:11px;width:7px;height:1px;background:var(--accent);}
@media (max-width:640px){
  .cr-role-head{grid-template-columns:1fr auto;gap:14px;padding:24px 2px;}
  .cr-role-meta{grid-column:1;grid-row:2;}
}

/* apply / open application */
#cr-apply{background:var(--dark);color:#fff;}
#cr-apply h3{color:#fff;}
#cr-apply .cr-apply-copy{font-size:15px;line-height:1.95;color:rgba(255,255,255,.6);
  font-weight:300;max-width:560px;margin-bottom:38px;}
.cr-apply-mail{font-family:var(--font-serif);font-size:clamp(22px,3vw,34px);
  color:var(--accent);border-bottom:1px solid rgba(255,255,255,.2);padding-bottom:6px;
  display:inline-block;transition:border-color .4s ease;}
.cr-apply-mail:hover{border-color:var(--accent);}
.cr-apply-note{margin-top:34px;font-size:12.5px;line-height:1.85;color:rgba(255,255,255,.42);
  font-weight:300;max-width:520px;}
.cr-apply-note a{color:var(--accent);border-bottom:1px solid rgba(255,255,255,.2);}

/* Apply-online form. Reuses the .field underline treatment from the
   enquiry form, re-tinted for the dark section it sits in. */
.cr-form{margin-top:8px;max-width:920px;}
.cr-form-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px 48px;}
@media (max-width:760px){.cr-form-grid{grid-template-columns:1fr;gap:4px;}}
.cr-field{border-bottom-color:rgba(255,255,255,.18);}
.cr-field input,.cr-field select{color:#fff;font-size:15px;}
.cr-field label{color:rgba(255,255,255,.5);}
.cr-field label i{font-style:normal;color:var(--accent);}
.cr-field input:focus ~ label,.cr-field input:not(:placeholder-shown) ~ label{color:var(--accent);}
.cr-field::after{background:var(--accent);}
/* Autofill in Chrome paints its own background — force it back to
   transparent so the field doesn't turn into a white block on a dark panel */
.cr-field input:-webkit-autofill,.cr-field input:-webkit-autofill:focus{
  -webkit-text-fill-color:#fff;transition:background-color 9999s ease-out;}

.cr-select select{width:100%;background:transparent;border:none;outline:none;
  font-family:var(--font-sans);font-weight:300;-webkit-appearance:none;appearance:none;
  padding-right:26px;cursor:pointer;}
.cr-select select option{background:var(--dark);color:#fff;}
.cr-select select:invalid{color:rgba(255,255,255,.5);}   /* placeholder state */
.cr-select::before{content:'';position:absolute;right:4px;top:50%;width:7px;height:7px;
  border-right:1px solid rgba(255,255,255,.5);border-bottom:1px solid rgba(255,255,255,.5);
  transform:translateY(-70%) rotate(45deg);pointer-events:none;}

.cr-file{margin-top:38px;}
.cr-file-label{display:block;font-size:12.5px;letter-spacing:.02em;color:rgba(255,255,255,.75);margin-bottom:14px;}
.cr-file-label i{font-style:normal;color:var(--accent);}
.cr-file-label em{font-style:normal;color:rgba(255,255,255,.42);}
.cr-file-drop{display:inline-flex;align-items:center;gap:16px;cursor:pointer;}
.cr-file-drop input[type="file"]{position:absolute;width:1px;height:1px;opacity:0;
  overflow:hidden;clip:rect(0 0 0 0);}
.cr-file-btn{font-size:10.5px;letter-spacing:.2em;text-transform:uppercase;color:#fff;
  border:1px solid rgba(255,255,255,.25);border-radius:999px;padding:13px 26px;
  transition:border-color .4s ease,background .4s ease;}
.cr-file-drop:hover .cr-file-btn{border-color:var(--accent);background:rgba(255,255,255,.05);}
.cr-file-name{font-size:13px;font-weight:300;color:rgba(255,255,255,.45);}
.cr-file-name.has-file{color:var(--accent);}

/* Attachment preview — lets the applicant (and anyone testing the form)
   open the exact file that will be sent, before sending it. */
.cr-file-preview{display:flex;align-items:center;gap:18px;margin-top:20px;
  padding:14px;border:1px solid rgba(255,255,255,.14);border-radius:3px;
  background:rgba(255,255,255,.03);max-width:460px;}
.cr-prev-thumb{width:66px;height:84px;flex:none;border-radius:2px;overflow:hidden;
  background:rgba(255,255,255,.06);display:flex;align-items:center;justify-content:center;
  border:1px solid rgba(255,255,255,.12);transition:border-color .35s ease;}
.cr-prev-thumb:hover{border-color:var(--accent);}
.cr-prev-thumb img{width:100%;height:100%;object-fit:cover;display:block;}
.cr-prev-thumb.is-pdf{font-size:11px;letter-spacing:.16em;color:var(--accent);}
.cr-prev-meta{display:flex;flex-direction:column;gap:3px;min-width:0;}
.cr-prev-name{font-size:13px;color:#fff;font-weight:300;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;}
.cr-prev-size{font-size:11px;color:rgba(255,255,255,.4);}
.cr-prev-open{font-size:10.5px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--accent);margin-top:6px;border-bottom:1px solid transparent;align-self:flex-start;}
.cr-prev-open:hover{border-bottom-color:var(--accent);}
.cr-prev-remove{background:none;border:0;padding:0;margin-top:2px;align-self:flex-start;
  font-family:var(--font-sans);font-size:10.5px;letter-spacing:.18em;text-transform:uppercase;
  color:rgba(255,255,255,.35);cursor:pointer;transition:color .3s ease;}
.cr-prev-remove:hover{color:#e08379;}
/* Badge shown when a file had to be shrunk to fit the 2 MB cap, so the
   applicant can see the version being emailed is not their original. */
.cr-prev-note{font-size:10.5px;letter-spacing:.04em;color:var(--accent);
  opacity:.85;margin-top:2px;}
.cr-prev-note.warn{color:#e0b579;}
/* Whole control is locked while a file is being compressed. */
.cr-file.is-busy{opacity:.6;pointer-events:none;}
.cr-file.is-busy .cr-file-btn{border-style:dashed;}
@media (max-width:480px){.cr-file-preview{max-width:none;}}
.cr-file-drop:focus-within .cr-file-btn{border-color:var(--accent);}

.cr-send{margin-top:40px;}
.cr-status{color:rgba(255,255,255,.55);}
.cr-status.ok{color:var(--accent);}
.cr-status.err{color:#e08379;}
.cr-field.is-invalid{border-bottom-color:#e08379;}

.pd-works-shell .filter-bar{margin-bottom:70px;}
.pd-works-intro{font-family:var(--font-sans);font-size:16.5px;line-height:1.95;color:var(--text-2);font-weight:300;max-width:640px;margin:0 0 36px;}
@media (max-width:640px){.pd-works-shell{padding:56px 0 100px;}}
.pd-story-body p.lede-split .line > div{transform:translateY(100%);}


.pd-credits{display:grid;grid-template-columns:repeat(3,1fr);gap:60px;padding:110px 48px 160px;}
.pd-credits .block .h{font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--accent);margin-bottom:18px;}
.pd-credits .block p{font-size:14px;line-height:2;color:var(--text-2);font-weight:300;}
@media (max-width:1023px){.pd-credits{grid-template-columns:1fr;padding:80px 24px 120px;}}

.pd-reveal{opacity:0;transform:translateY(80px);}
.pd-about-shell{background:var(--bg);}
.pd-about-shell .ab-section{padding:140px 0;}
.pd-about-shell .ab-section:first-of-type{padding-top:120px;}
.pd-about-shell .ab-section:last-of-type{padding-bottom:170px;}
/* Without a hero the content now starts at the very top of the overlay,
   so the first section needs enough headroom to clear the fixed Back
   button sitting at top:32px rather than tucking underneath it. */
.pd-about-shell.pd-about-nohero .ab-section:first-of-type{padding-top:150px;}
@media (max-width:768px){
  .pd-about-shell.pd-about-nohero .ab-section:first-of-type{padding-top:120px;}
}

/* About section starts collapsed — only the original preview block is
   visible until the "About" nav item expands it. */
#aboutExtra{height:0;overflow:hidden;opacity:0;will-change:height,opacity;transition:height .9s cubic-bezier(.16,1,.3,1),opacity .6s ease;}

/* ================= ABOUT — EXTENDED SUB-SECTIONS ================= */
.ab-section{position:relative;padding:150px 0;}
@media (max-width:1023px){.ab-section{padding:100px 0;}}
.ab-split{display:grid;grid-template-columns:.85fr 1.15fr;gap:90px;align-items:center;}
.ab-split.rev{grid-template-columns:1.15fr .85fr;}
.ab-split.rev .ab-imgcol{order:2;}
@media (max-width:1023px){.ab-split,.ab-split.rev{grid-template-columns:1fr;gap:48px;}.ab-split.rev .ab-imgcol{order:0;}}
.ab-imgcol{position:relative;}
.ab-imgcol .ab-frame{position:relative;overflow:hidden;border-radius:2px;}
.ab-imgcol .ab-frame img{width:100%;height:100%;object-fit:cover;aspect-ratio:4/5;}
.ab-imgcol .ab-num{position:absolute;bottom:-16px;left:-16px;background:var(--bg-2);border:1px solid var(--border);padding:12px 20px;font-size:11px;letter-spacing:.14em;color:var(--text-2);}
.ab-copycol h2{font-size:clamp(28px,4.4vw,52px);line-height:1.15;margin:18px 0 28px;}
.ab-copycol h2 em{font-style:italic;color:var(--accent);}
.ab-lines p{overflow:hidden;}
.ab-lines p span{display:inline-block;transform:translateY(110%);}
.ab-copycol .ab-lines p{font-size:16px;line-height:1.85;color:var(--text-2);font-weight:300;}
.ab-portrait{position:relative;overflow:hidden;}
.ab-portrait img{clip-path:inset(0 0 100% 0);}
/* The founder photo is a 2:3 frame sitting in a 4:5 window, so ~17% of its
   height gets trimmed. Anchoring to the top takes that off the bottom
   (chair base, lower legs) instead of splitting it and clipping his head. */
/* The founder photograph is a 2:3 frame; showing it in a 2:3 window means
   nothing is cropped away, so no object-position nudge is needed. */
#ab-founder .ab-frame img{aspect-ratio:2/3;object-position:50% 50%;}
.ab-role{color:var(--accent);font-size:13px;letter-spacing:.08em;margin-top:6px;display:block;}
/* Sized deliberately below the section h2 above it: the sentence is still
   the headline, the name is the subject being introduced. Sharing .ab-role
   with the Leadership block keeps the name/role pairing identical in both
   places rather than inventing a second treatment for the same thing. */
.ab-copycol .ab-name{font-size:clamp(24px,2.8vw,36px);line-height:1.15;margin:0 0 26px;}
.ab-quote{font-family:var(--font-serif);font-style:italic;font-size:clamp(20px,2.4vw,27px);line-height:1.5;color:var(--text);margin:26px 0 26px;}
.ab-sig{font-family:var(--font-serif);font-size:22px;color:var(--accent);}

/* our story timeline */
#ab-story{background:var(--dark);color:#fff;}
#ab-story .eyebrow{color:rgba(255,255,255,.5);}
#ab-story h2{color:#fff;font-size:clamp(28px,4.4vw,52px);margin:18px 0 70px;max-width:640px;}
#ab-story h2 em{font-style:italic;color:var(--accent);}
.ab-titem{display:grid;grid-template-columns:120px 1px 1fr;gap:36px;padding:36px 0;border-bottom:1px solid rgba(255,255,255,.08);}
.ab-titem:first-child{padding-top:0;}
.ab-titem .ab-yr{font-family:var(--font-serif);font-size:30px;color:var(--accent);}
.ab-titem .ab-rail{position:relative;background:rgba(255,255,255,.1);}
.ab-titem .ab-rail i{position:absolute;top:6px;left:50%;transform:translateX(-50%);width:9px;height:9px;border-radius:50%;background:var(--accent);}
.ab-titem h4{font-size:21px;font-weight:400;margin-bottom:8px;color:#fff;}
.ab-titem p{font-size:13.5px;line-height:1.75;color:rgba(255,255,255,.55);max-width:560px;}
@media (max-width:640px){.ab-titem{grid-template-columns:70px 1px 1fr;gap:20px;}}

/* team */
#ab-team{background:var(--bg-2);}
/* Centre the eyebrow + heading over the grid, so the whole block reads as
   one centred composition. Card text stays left-aligned — centred body
   copy under a photo is harder to read and looks unresolved. */
#ab-team .wrap{text-align:center;}
#ab-team .wrap > h2{margin-left:auto;margin-right:auto;}
#ab-team .ab-tcard{text-align:left;}
/* Three columns, per the sketched layout. The cap keeps each card about
   the same physical size it had in the old two-up grid rather than letting
   three stretch across the full .wrap width. */
.ab-team-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:56px 44px;margin:64px auto 0;
  max-width:1120px;}
@media (max-width:1023px){.ab-team-grid{grid-template-columns:repeat(2,1fr);max-width:760px;gap:48px 40px;}}
@media (max-width:640px){.ab-team-grid{grid-template-columns:1fr;gap:44px;max-width:400px;}}
.ab-tcard{position:relative;}
/* Portraits are pre-built at exactly 4:5 — trimmed just below the waistband,
   crowns on a common line — so the frame must match that ratio exactly; any
   other value would re-crop them in the browser and undo the alignment. */
.ab-tcard .ab-ph{position:relative;overflow:hidden;border-radius:2px;aspect-ratio:4/5;background:var(--bg);}
.ab-tcard .ab-ph img{width:100%;height:100%;object-fit:cover;object-position:50% 50%;transform:scale(1);transition:transform .8s cubic-bezier(.16,1,.3,1),filter .5s ease;}
/* A final card left alone on its own row would sit against the left edge,
   so centre it. 3n+1 as the last child means the count leaves a remainder
   of one — the only case where a single card is stranded. The two-column
   breakpoint needs the odd-count test instead. */
.ab-team-grid .ab-tcard:last-child:nth-child(3n+1){grid-column:1 / -1;justify-self:center;width:calc(33.333% - 30px);}
@media (max-width:1023px){
  .ab-team-grid .ab-tcard:last-child:nth-child(3n+1){grid-column:auto;justify-self:stretch;width:auto;}
  .ab-team-grid .ab-tcard:last-child:nth-child(odd){grid-column:1 / -1;justify-self:center;width:calc(50% - 20px);}
}
@media (max-width:640px){
  .ab-team-grid .ab-tcard:last-child:nth-child(odd),
  .ab-team-grid .ab-tcard:last-child:nth-child(3n+1){grid-column:auto;justify-self:stretch;width:auto;}
}
.ab-tcard:hover .ab-ph img{transform:scale(1.06);filter:saturate(1.08);}
.ab-tcard .ab-ph::after{content:'';position:absolute;inset:0;background:linear-gradient(180deg,transparent 55%,rgba(0,0,0,.42) 100%);opacity:0;transition:opacity .5s ease;}
/* Fade-in once each About-page image has actually finished loading/decoding,
   rather than popping in abruptly. Class is toggled by JS on 'load' (or
   immediately if the image is already cached/complete). This is additive —
   it does not replace or fight the existing reveal/clip-path animations
   already on these elements. */
#about img, #aboutExtra img{opacity:0;transition:opacity .45s ease;}
#about img.ab-img-loaded, #aboutExtra img.ab-img-loaded{opacity:1;}
.ab-tcard:hover .ab-ph::after{opacity:1;}
.ab-tcard .ab-info{padding:20px 2px 0;transform:translateY(0);transition:transform .5s cubic-bezier(.16,1,.3,1);}
.ab-tcard:hover .ab-info{transform:translateY(-4px);}
.ab-tcard h4{font-size:19px;font-weight:400;margin-bottom:4px;}
.ab-tcard .ab-pos{display:block;font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--accent);margin-bottom:10px;}
.ab-tcard p{font-size:12.5px;line-height:1.7;color:var(--text-2);font-weight:300;}

/* team group photo — wide banner that opens the About page.
   Caption sits under the frame on a hairline rule (not overlaid), so the
   label reads as a plain figure caption. */
/* Widened a little past the .wrap gutters so the group shot reads as the
   opening statement of the page rather than sitting inside the text
   column. Clamped so it never runs to the very edge of the viewport. */
/* The photo breaks out of the text column to sit as the opening statement
   of the page. Two separate jobs, deliberately on two separate elements:

   the OUTER element does the full-bleed centring and is never animated,
   because centring an over-wide block needs transform:translateX(-50%),
   and the reveal animation also writes to transform. When both lived on
   one element the animation's inline transform replaced the centring and
   the photo jumped sideways once it faded in — which is why it only went
   off-centre some of the time.

   the INNER element carries .reveal and owns transform outright. */
.ab-teamphoto{position:relative;width:min(calc(100vw - 40px), 1800px);
  margin-left:50%;transform:translateX(-50%);}
.ab-tp-inner{display:block;}
.ab-teamphoto .ab-tp-frame{position:relative;overflow:hidden;border-radius:2px;background:var(--bg);}
/* The frame matches the photo's own 2600x1288 proportions, and the image is
   left at true scale. A resting scale(1.03) used to zoom the picture by 3%,
   which quietly cropped roughly 40px off each side and cut the people
   standing at either end out of the group. A hover zoom would do the same,
   so this photo simply doesn't scale. */
.ab-teamphoto .ab-tp-frame img{width:100%;height:100%;display:block;aspect-ratio:2600/1288;object-fit:cover;
  transform:none;transition:opacity .6s ease;}
.ab-teamphoto:hover .ab-tp-frame img{transform:none;}
.ab-teamphoto .ab-tp-cap{display:block;margin-top:22px;padding-top:18px;border-top:1px solid var(--border);
  font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:var(--text-2);}
@media (max-width:640px){
  .ab-teamphoto{width:100%;margin-left:0;transform:none;}
  /* Keeping the photo's real proportions here too — forcing 16/10 on a
     phone cropped the end of the row off entirely. */
  .ab-teamphoto .ab-tp-frame img{aspect-ratio:2600/1288;}
  .ab-teamphoto .ab-tp-cap{margin-top:16px;padding-top:14px;font-size:10px;}
}

/* full-width divider between the founder row and the team grid */
.ab-rule{height:1px;background:var(--border);}

/* values */
.ab-values-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--border);border:1px solid var(--border);margin-top:70px;}
.ab-vcard{background:var(--bg-2);padding:44px 30px;transition:transform .5s cubic-bezier(.16,1,.3,1),box-shadow .5s ease,background .5s ease,border-color .5s ease;}
.ab-vcard .ab-n{font-size:11px;color:var(--text-2);letter-spacing:.18em;margin-bottom:40px;display:block;}
.ab-vcard h3{font-size:22px;font-weight:400;margin-bottom:12px;}
.ab-vcard p{font-size:13px;color:var(--text-2);line-height:1.75;font-weight:300;}
.ab-vcard:hover{transform:translateY(-8px);box-shadow:0 34px 70px rgba(0,0,0,.10);background:var(--glass-bg);border-bottom:2px solid var(--accent);}
@media (max-width:1023px){.ab-values-grid{grid-template-columns:repeat(2,1fr);}}
@media (max-width:640px){.ab-values-grid{grid-template-columns:1fr;}}

/* about CTA */
#ab-cta{background:var(--dark);color:#fff;text-align:center;padding:200px 0;overflow:hidden;position:relative;}
#ab-cta .ab-grid-bg{position:absolute;inset:0;opacity:.05;background-image:linear-gradient(rgba(255,255,255,.6) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.6) 1px,transparent 1px);background-size:80px 80px;}
#ab-cta .eyebrow{color:rgba(255,255,255,.5);margin-bottom:30px;}
#ab-cta h2{position:relative;font-size:clamp(32px,6.4vw,80px);line-height:1.12;font-weight:400;max-width:900px;margin:0 auto 46px;}
#ab-cta h2 em{font-style:italic;color:var(--accent);}

@media (prefers-reduced-motion: reduce){
  *{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important;}
}

/* ================= IMAGE WATERMARK + LIGHTBOX + PROTECTION ================= */

/* The mark itself is a white, transparent-background PNG built from the
   supplied logo. It is applied as a background on a pseudo-element rather
   than baked into the photos, so one asset serves every image and the
   photos themselves stay untouched. A soft shadow keeps it legible over
   pale and dark areas alike. */
:root{
  --wm: url("assets/b71a05f10987-520x262.png");
}
.wm-mark{position:relative;}
.wm-mark::after{
  content:'';position:absolute;right:3.2%;bottom:3.6%;
  width:clamp(78px, 17%, 190px);aspect-ratio:520/262;
  background:var(--wm) no-repeat center/contain;
  opacity:.55;pointer-events:none;z-index:3;
  filter:drop-shadow(0 1px 3px rgba(0,0,0,.55));
  transition:opacity .4s ease;
}
.wm-mark:hover::after{opacity:.72;}
/* Small tiles (team portraits, thumbnails) get a proportionally smaller
   mark so it never dominates the picture. */
.wm-mark.wm-sm::after{width:clamp(54px, 26%, 92px);opacity:.5;}
@media (max-width:640px){
  .wm-mark::after{width:clamp(60px, 26%, 120px);right:4%;bottom:4%;}
}

/* ---- download deterrents ----
   These stop the ordinary routes: right-click > Save image, dragging the
   picture to the desktop, and long-press on touch. They are deterrents,
   not security -- see the note in the JS below. */
.no-save img, img.no-save{
  -webkit-user-select:none;-moz-user-select:none;user-select:none;
  -webkit-user-drag:none;-khtml-user-drag:none;-moz-user-drag:none;-o-user-drag:none;
  -webkit-touch-callout:none;
  pointer-events:none;                 /* right-click lands on the wrapper */
}
/* A transparent sheet over each protected image. "Save image as" on this
   element yields nothing, because the element is not an image. */
.no-save{position:relative;}
.no-save::before{
  content:'';position:absolute;inset:0;z-index:2;
  background:transparent;cursor:zoom-in;
}

/* ---- lightbox ---- */
#lightbox{
  position:fixed;inset:0;z-index:9000;display:none;
  background:rgba(8,8,8,.96);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  opacity:0;transition:opacity .38s ease;
  align-items:center;justify-content:center;
}
#lightbox.is-open{display:flex;}
#lightbox.is-visible{opacity:1;}
#lightbox .lb-stage{
  position:relative;width:min(84vw,1400px);height:min(86vh,1000px);
  display:flex;align-items:center;justify-content:center;
}
#lightbox .lb-figure{
  position:relative;max-width:100%;max-height:100%;
  display:flex;align-items:center;justify-content:center;
  transition:opacity .3s ease, transform .45s cubic-bezier(.16,1,.3,1);
}
#lightbox.is-swapping .lb-figure{opacity:0;transform:scale(.985);}
#lightbox img{
  max-width:100%;max-height:86vh;width:auto;height:auto;display:block;
  object-fit:contain;border-radius:2px;
  box-shadow:0 40px 120px rgba(0,0,0,.6);
}
#lightbox .lb-btn{
  position:absolute;top:50%;transform:translateY(-50%);
  width:56px;height:56px;border-radius:50%;
  background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.22);
  color:#fff;display:flex;align-items:center;justify-content:center;
  cursor:pointer;z-index:5;
  transition:background .3s ease,border-color .3s ease,transform .3s ease;
}
#lightbox .lb-btn:hover{background:rgba(255,255,255,.16);border-color:var(--accent-bright);}
#lightbox .lb-btn:focus-visible{outline:2px solid var(--accent-bright);outline-offset:3px;}
/* Pinned to the viewport, not the stage, so they can never be clipped
   off-screen by a wide image. */
#lightbox .lb-prev,
#lightbox .lb-next{position:fixed;}
#lightbox .lb-prev{left:22px;}
#lightbox .lb-next{right:22px;}
#lightbox .lb-btn svg,
#lightbox .lb-close svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;}
#lightbox .lb-close{
  position:fixed;top:26px;right:30px;width:48px;height:48px;border-radius:50%;
  background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.22);
  color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:6;
  transition:background .3s ease,border-color .3s ease;
}
#lightbox .lb-close:hover{background:rgba(255,255,255,.16);border-color:var(--accent-bright);}
#lightbox .lb-close:focus-visible{outline:2px solid var(--accent-bright);outline-offset:3px;}
#lightbox .lb-meta{
  position:fixed;left:0;right:0;bottom:22px;text-align:center;color:rgba(255,255,255,.62);
  font-size:11px;letter-spacing:.2em;text-transform:uppercase;pointer-events:none;
}
#lightbox .lb-count{color:var(--accent-bright);}
@media (max-width:900px){
  #lightbox .lb-prev{left:8px;}
  #lightbox .lb-next{right:8px;}
  #lightbox .lb-btn{width:46px;height:46px;}
  #lightbox .lb-stage{width:96vw;}
  #lightbox .lb-close{top:16px;right:16px;}
}
@media (prefers-reduced-motion: reduce){
  #lightbox,#lightbox .lb-figure{transition:none;}
}



/* ==========================================================================
   ADDITIONS — accessibility, hero calls to action, project navigation
   ========================================================================== */

/* Skip link: off-screen until focused. */
.skip-link{position:fixed;top:12px;left:12px;z-index:11000;padding:12px 18px;background:var(--dark);color:#fff;
  font-size:12px;letter-spacing:.14em;text-transform:uppercase;transform:translateY(-160%);transition:transform .3s var(--ease-out);}
.skip-link:focus{transform:translateY(0);}

/* One visible focus style for keyboard users everywhere. */
:focus-visible{outline:var(--focus);outline-offset:3px;}
header#nav a:focus-visible,header#nav button:focus-visible{outline-color:var(--accent-bright);}
.pcard:focus-visible{outline:none;}
.pcard:focus-visible .frame{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent);}
.pcard:focus-visible .caption h3{color:var(--accent);}
header#nav a.logo{display:inline-block;}

/* Honeypot field — present for bots, invisible to people and screen readers. */
.hp-field{position:absolute!important;left:-9999px!important;width:1px;height:1px;opacity:0;}

/* Inline field error state (enquiry form). */
.field.is-invalid{border-bottom-color:#b4443a;}
.field.is-invalid label{color:#b4443a;}

/* ---------- HERO: supporting line + calls to action ---------- */
#hero .hero-sub{margin-top:26px;max-width:520px;font-family:var(--font-sans);font-size:15px;line-height:1.75;
  font-weight:300;color:rgba(255,255,255,.82);letter-spacing:.01em;text-shadow:0 1px 14px rgba(0,0,0,.45);}
#hero .hero-ctas{margin-top:38px;display:flex;align-items:center;justify-content:center;gap:34px;flex-wrap:wrap;}
#hero .hero-btn{border-color:rgba(255,255,255,.55);}
#hero .hero-link{display:inline-flex;align-items:center;gap:10px;color:#fff;font-size:11px;letter-spacing:.2em;
  text-transform:uppercase;padding:8px 0;position:relative;}
#hero .hero-link::after{content:'';position:absolute;left:0;bottom:0;width:100%;height:1px;background:var(--accent-bright);
  transform:scaleX(.35);transform-origin:left;transition:transform .55s var(--ease-out);}
#hero .hero-link:hover::after,#hero .hero-link:focus-visible::after{transform:scaleX(1);}
#hero .hero-link .arrow{transition:transform .55s var(--ease-out);}
#hero .hero-link:hover .arrow{transform:translateX(6px);}
#hero .scroll-ind{color:rgba(255,255,255,.7);}
#hero .scroll-ind .line{display:block;}
#hero .scroll-ind:hover span{color:#fff;}
@media (max-width:640px){
  #hero .hero-sub{font-size:14px;margin-top:20px;}
  #hero .hero-ctas{margin-top:30px;gap:22px;flex-direction:column;}
  #hero .scroll-ind{bottom:24px;}
}
@media (max-height:620px){ #hero .scroll-ind{display:none;} }

/* ---------- PROJECT DETAIL: previous / next ---------- */
.pd-pager{display:grid;grid-template-columns:1fr 1fr;border-bottom:1px solid var(--border);background:var(--bg-2);}
.pd-pager button{all:unset;box-sizing:border-box;cursor:pointer;display:flex;flex-direction:column;gap:12px;
  padding:72px var(--gutter);position:relative;overflow:hidden;transition:background .5s ease;}
.pd-pager button + button{border-left:1px solid var(--border);text-align:right;align-items:flex-end;}
.pd-pager .pg-label{font-size:10.5px;letter-spacing:.24em;text-transform:uppercase;color:var(--text-2);}
.pd-pager .pg-title{font-family:var(--font-serif);font-size:clamp(26px,3.2vw,44px);line-height:1.1;color:var(--text);
  transition:color .4s ease,transform .6s var(--ease-out);}
.pd-pager .pg-meta{font-size:12px;color:var(--text-2);}
.pd-pager button:hover{background:var(--bg);}
.pd-pager button:hover .pg-title,.pd-pager button:focus-visible .pg-title{color:var(--accent);}
.pd-pager button:first-child:hover .pg-title{transform:translateX(-6px);}
.pd-pager button + button:hover .pg-title{transform:translateX(6px);}
.pd-pager button:focus-visible{outline:var(--focus);outline-offset:-6px;}
.pd-closer{display:flex;justify-content:center;padding:70px 24px 110px;}
@media (max-width:768px){
  .pd-pager{grid-template-columns:1fr;}
  .pd-pager button{padding:44px var(--gutter-sm);}
  .pd-pager button + button{border-left:none;border-top:1px solid var(--border);}
}

/* Small phones: keep the wordmark on one line and the hero eyebrow inside
   the screen (at 390px the third label used to run off the right edge). */
#hero .hero-sub{text-wrap:balance;}
@media (max-width:480px){
  header#nav .logo{font-size:15px;letter-spacing:.26em;white-space:nowrap;}
  #hero .eyebrow-row{gap:10px;flex-wrap:wrap;justify-content:center;}
  #hero .eyebrow-row span{font-size:10px;letter-spacing:.22em;}
}
/* 320–360px: the 15px wordmark plus the "Close ✕" button is wider than the
   row, which pushed the ✕ off-screen and ran the label into the logo. */
@media (max-width:360px){
  header#nav{gap:12px;}
  header#nav .logo{font-size:13px;letter-spacing:.2em;}
  header#nav .menu-btn{padding:6px 0 6px 6px;gap:8px;flex-shrink:0;}
}

/* Logo and Menu/Close stay positioned (focus outlines, the bar animation);
   the header itself is lifted above the open mobile panel via
   body.menu-open, see MOBILE MENU. */
header#nav .logo,header#nav .menu-btn{position:relative;}
