/* ==========================================================================
   Jacob Muehlbach Photography
   Direction: "Full Bleed" (see .design/drafts/homepage-redesign/v3-full-bleed.html)

   The idea: no page margin, no gutters. Photographs tile edge to edge and
   text floats on top of them in a frosted scrim. Chrome stays near-neutral
   so the only saturated colour on the page comes from the images.
   ========================================================================== */

/* Inter, self-hosted. Variable weight 200-700, latin subsets only.
   SIL Open Font License 1.1. The latin-ext file is only fetched if a
   character in its range actually appears, so English pages never pay for it. */
@font-face{
  font-family:"Inter"; font-style:normal; font-weight:200 700; font-display:swap;
  src:url("../fonts/inter-latin-var.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Inter"; font-style:normal; font-weight:200 700; font-display:swap;
  src:url("../fonts/inter-latin-ext-var.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,
    U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* ── tokens ─────────────────────────────────────────────────────────────── */
:root{
  --stone:#111113;          /* page ground */
  --stone-2:#1b1b1f;        /* panels, cards, empty image slots */
  --stone-3:#26262b;        /* hairlines */
  --fg:#ffffff;
  --fg-dim:rgba(255,255,255,.68);
  --fg-faint:rgba(255,255,255,.55);

  --edge:clamp(16px,3vw,34px);   /* page inset for text; imagery ignores it */
  --gap:2px;                     /* mosaic gutter. Deliberately tiny. */
  --maxw:1180px;                 /* prose and forms */
  --maxw-wide:2100px;            /* image grids, so tiles stay sane at 2560 */
  --radius:0px;
  --header-h:60px;

  --sans:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  --ease:cubic-bezier(.2,.7,.2,1);
}

/* ── reset ──────────────────────────────────────────────────────────────── */
*{box-sizing:border-box;}
figure{margin:0;}     /* UA default is 1em 40px, which would gut the mosaic */
html{scroll-behavior:smooth; background:var(--stone);}
@media (prefers-reduced-motion:reduce){ html{scroll-behavior:auto;} }
body{
  margin:0; background:var(--stone); color:var(--fg);
  font-family:var(--sans); font-size:16px; line-height:1.6;
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
  overflow-x:hidden;   /* belt and braces; nothing should overflow anyway */
}
img{display:block; max-width:100%;}
a{color:inherit; text-decoration:none;}
button{font:inherit; color:inherit; background:none; border:0; cursor:pointer;}
h1,h2,h3,h4{margin:0; font-weight:200; letter-spacing:-.03em; line-height:1.02;}
p{margin:0 0 1em;}

:focus-visible{outline:2px solid #fff; outline-offset:3px;}

.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;
}
.skip{
  position:absolute; left:var(--edge); top:-60px; z-index:200;
  background:#fff; color:#111; padding:10px 18px; font-size:14px; font-weight:600;
  transition:top .2s;
}
.skip:focus{top:12px;}

/* ── shared type ────────────────────────────────────────────────────────── */
.label{
  font-size:11px; letter-spacing:.24em; text-transform:uppercase;
  color:var(--fg-dim); font-weight:500;
}
.display{font-weight:200; letter-spacing:-.035em; line-height:.98;}
.lede{color:var(--fg-dim); font-size:clamp(15px,1.5vw,17px); max-width:52ch;}

.wrap{max-width:var(--maxw); margin-inline:auto; padding-inline:var(--edge);}
.wrap-wide{max-width:var(--maxw-wide); margin-inline:auto;}
.section{padding-block:clamp(56px,7vw,104px);}
.section-head{margin-bottom:clamp(26px,3.4vw,44px);}
.section-head h2{font-size:clamp(28px,3.8vw,52px); margin:12px 0 0;}
.section-head p{margin:14px 0 0;}

/* ── header ─────────────────────────────────────────────────────────────── */
.site-header{
  position:fixed; inset:0 0 auto 0; z-index:90;
  display:flex; align-items:center; justify-content:space-between;
  gap:16px; padding:12px var(--edge); min-height:var(--header-h);
  /* No background and no border here. Both live on the pseudo-elements
     below so they can fade out downward instead of ending on a hard edge. */
}
/* Both layers overhang the header by 30px, which gives the gradients room to
   reach zero below the header rather than at its edge. That overhang is the
   whole trick: end either gradient at the header's own bottom and you get
   back the line we were trying to remove. */
.site-header::before,
.site-header::after{
  content:""; position:absolute; inset:0 0 -30px 0; pointer-events:none;
}
/* Blur, strongest at the top, masked to nothing by the bottom. backdrop-filter
   takes no gradient of its own, so the layer is uniformly blurred and then
   masked. Many closely spaced stops, because a short ramp between distant
   stops renders as a visible band rather than a fade. */
.site-header::before{
  -webkit-backdrop-filter:blur(20px) saturate(1.25);
  backdrop-filter:blur(20px) saturate(1.25);
  -webkit-mask-image:linear-gradient(to bottom,
    rgba(0,0,0,1) 0%, rgba(0,0,0,.99) 14%, rgba(0,0,0,.94) 28%,
    rgba(0,0,0,.85) 40%, rgba(0,0,0,.7) 52%, rgba(0,0,0,.52) 63%,
    rgba(0,0,0,.34) 74%, rgba(0,0,0,.18) 84%, rgba(0,0,0,.07) 92%,
    rgba(0,0,0,0) 100%);
  mask-image:linear-gradient(to bottom,
    rgba(0,0,0,1) 0%, rgba(0,0,0,.99) 14%, rgba(0,0,0,.94) 28%,
    rgba(0,0,0,.85) 40%, rgba(0,0,0,.7) 52%, rgba(0,0,0,.52) 63%,
    rgba(0,0,0,.34) 74%, rgba(0,0,0,.18) 84%, rgba(0,0,0,.07) 92%,
    rgba(0,0,0,0) 100%);
}
/* Darkening scrim on the same falloff. Blur alone does not keep white text
   legible when a bright photo scrolls underneath. */
.site-header::after{
  /* Held near .75 through the top ~45%, which is the band the wordmark and
     nav actually occupy, then dropped away. Over a blown-out photo that keeps
     the 12px nav links at 5.1:1; letting it decay linearly from the top put
     them at 3.8:1. The old flat box was worse still, at 1.8:1. */
  background:linear-gradient(to bottom,
    rgba(12,12,14,.80) 0%, rgba(12,12,14,.78) 20%, rgba(12,12,14,.74) 35%,
    rgba(12,12,14,.66) 48%, rgba(12,12,14,.52) 60%, rgba(12,12,14,.36) 72%,
    rgba(12,12,14,.22) 82%, rgba(12,12,14,.12) 90%, rgba(12,12,14,.04) 96%,
    rgba(12,12,14,0) 100%);
}
/* Children sit above both layers. Without this the scrim would paint over
   the wordmark and nav, since ::after is generated as the last child.
   z-index 3 specifically, because the open mobile panel sits at 1 and has to
   pass underneath the wordmark and the close button. */
.site-header > *{position:relative; z-index:3;}
/* Pages without a full-bleed hero need the fixed header's height back. */
.has-offset{padding-top:var(--header-h);}

.wordmark{
  font-size:13px; letter-spacing:.2em; text-transform:uppercase;
  font-weight:500; line-height:1.45; white-space:nowrap;
  /* inline-flex + min-height so the logo link clears a 44px touch target */
  display:inline-flex; flex-direction:column; justify-content:center; min-height:44px;
}
.wordmark span{display:block; font-size:9.5px; letter-spacing:.32em; color:var(--fg-dim);}

.nav{display:flex; align-items:center; gap:clamp(14px,2vw,28px);}
.nav a{
  font-size:12px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--fg-dim); transition:color .2s;
  display:inline-flex; align-items:center; min-height:44px;  /* touch target */
}
.nav a:hover{color:var(--fg);}
.nav a[aria-current="page"]{color:var(--fg);}
/* `.nav a` (0,1,1) outranks `.btn` (0,1,0), so without this the button's text
   inherits --fg-dim and goes near-invisible on its own white background. */
.nav a.btn{color:#111; letter-spacing:.1em; text-transform:none;}
.nav a.btn:hover{color:#111;}

.btn{
  display:inline-flex; align-items:center; justify-content:center;
  min-height:44px; padding:11px 22px; border-radius:999px;
  font-size:12.5px; letter-spacing:.1em; font-weight:600;
  background:rgba(255,255,255,.94); color:#111; transition:background .2s, transform .2s;
}
.btn:hover{background:#fff;}
.btn-lg{padding:15px 38px; font-size:13px;}
.btn-ghost{
  background:transparent; color:var(--fg);
  border:1px solid rgba(255,255,255,.42);
}
.btn-ghost:hover{background:rgba(255,255,255,.1); border-color:#fff;}

/* mobile menu */
.nav-toggle{
  display:none; width:44px; height:44px; align-items:center; justify-content:center;
}
.nav-toggle svg{width:20px; height:20px;}
.nav-toggle .ico-close{display:none;}
.nav-toggle[aria-expanded="true"] .ico-open{display:none;}
.nav-toggle[aria-expanded="true"] .ico-close{display:block;}

@media (max-width:820px){
  /* All of this is gated on .js. Without JavaScript the toggle cannot work,
     and an ungated collapse would leave a phone visitor with no navigation
     at all. No-JS falls through to a plain wrapped row below. */
  .js .nav-toggle{display:inline-flex;}

  /* The panel runs from the very top of the screen, not from under the
     header, so the scrim reads as one gradient falling down the page rather
     than as a separate slab hanging off a bar. The wordmark and the close
     button sit above it on z-index 3. */
  .js .nav{
    position:fixed; inset:0 0 auto 0; z-index:1;
    flex-direction:column; align-items:stretch; gap:0;
    padding:calc(var(--header-h) + 20px) var(--edge) 44px;
    background:none;
    /* Only visibility is toggled here. Opacity stays put so the per-link
       stagger below is not fighting a fade on the whole container. */
    visibility:hidden;
    transition:visibility 0s linear .5s;
  }
  .js .nav.open{visibility:visible; transition:visibility 0s;}

  /* Two layers, same split as the header: ::before is uniformly blurred and
     masked to a falloff, ::after is the darkening scrim. Both are wiped down
     from the top with clip-path, which reveals the gradient without
     stretching it the way a scaleY would. */
  .js .nav::before,
  .js .nav::after{
    content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
    clip-path:inset(0 0 100% 0);
    transition:clip-path .58s cubic-bezier(.2,.7,.2,1);
  }
  .js .nav.open::before,
  .js .nav.open::after{clip-path:inset(0 0 0 0);}
  .js .nav::before{
    -webkit-backdrop-filter:blur(24px) saturate(1.2);
    backdrop-filter:blur(24px) saturate(1.2);
    -webkit-mask-image:linear-gradient(to bottom,
      rgba(0,0,0,1) 0%, rgba(0,0,0,1) 55%, rgba(0,0,0,.96) 68%,
      rgba(0,0,0,.86) 78%, rgba(0,0,0,.66) 86%, rgba(0,0,0,.42) 92%,
      rgba(0,0,0,.2) 96%, rgba(0,0,0,.06) 99%, rgba(0,0,0,0) 100%);
    mask-image:linear-gradient(to bottom,
      rgba(0,0,0,1) 0%, rgba(0,0,0,1) 55%, rgba(0,0,0,.96) 68%,
      rgba(0,0,0,.86) 78%, rgba(0,0,0,.66) 86%, rgba(0,0,0,.42) 92%,
      rgba(0,0,0,.2) 96%, rgba(0,0,0,.06) 99%, rgba(0,0,0,0) 100%);
  }
  .js .nav::after{
    background:linear-gradient(to bottom,
      rgba(12,12,14,.94) 0%, rgba(12,12,14,.93) 40%, rgba(12,12,14,.91) 58%,
      rgba(12,12,14,.86) 70%, rgba(12,12,14,.76) 79%, rgba(12,12,14,.6) 86%,
      rgba(12,12,14,.4) 92%, rgba(12,12,14,.2) 96%, rgba(12,12,14,.07) 99%,
      rgba(12,12,14,0) 100%);
  }

  /* While the menu is open the header's own blur and scrim step aside, so
     there is one gradient from the top of the screen instead of two stacked
     falloffs fighting each other in the first 100px. */
  .menu-open .site-header::before,
  .menu-open .site-header::after{opacity:0;}
  .site-header::before,
  .site-header::after{transition:opacity .3s;}

  /* No dividers. The stagger already gives the list its rhythm, and the rules
     were the last hairlines left in the chrome. */
  .js .nav a{
    position:relative; z-index:1;
    font-size:15px; letter-spacing:.1em; min-height:54px;
    justify-content:flex-end; text-align:right;
    opacity:0; transform:translateX(30px);
    transition:opacity .4s var(--ease), transform .4s var(--ease);
  }
  .js .nav.open a{opacity:1; transform:none;}
  /* Staggered against the wipe, so each link arrives just behind the edge of
     the gradient as it passes. Delays only on .open, so closing collapses
     everything at once instead of playing the sequence backwards. */
  .js .nav.open a:nth-child(1){transition-delay:.10s;}
  .js .nav.open a:nth-child(2){transition-delay:.17s;}
  .js .nav.open a:nth-child(3){transition-delay:.24s;}
  .js .nav.open a:nth-child(4){transition-delay:.31s;}
  .js .nav.open a:nth-child(5){transition-delay:.38s;}
  /* In the menu the booking link is just another item in the list. As a white
     pill among four plain labels it read as a foreign object. It keeps the
     button treatment everywhere else, including the header on desktop.
     Needs (0,3,1) to outrank `.nav a.btn`, which sets the dark pill text. */
  .js .nav a.btn{
    margin-top:0; align-self:stretch;
    background:none; border-radius:0; padding:0;
    color:var(--fg-dim); font-weight:400;
    font-size:15px; letter-spacing:.1em; text-transform:uppercase;
    justify-content:flex-end; min-height:54px;
  }
  .js .nav a.btn:hover{background:none; color:var(--fg);}

  @media (prefers-reduced-motion:reduce){
    .js .nav::before, .js .nav::after{transition:none;}
    .js .nav a{transform:none; transition:opacity .15s;}
    .js .nav.open a{transition-delay:0s;}
  }

  /* No-JS fallback: every link stays on screen as a wrapped row. */
  html:not(.js) .site-header{flex-wrap:wrap;}
  html:not(.js) .nav{flex-wrap:wrap; gap:10px 18px; width:100%; padding-bottom:6px;}
  html:not(.js) .nav a{min-height:38px;}
}

/* ── hero ───────────────────────────────────────────────────────────────── */
.hero{position:relative; min-height:600px; height:100svh; overflow:hidden;}
.hero-slides{position:absolute; inset:0; margin:0; padding:0;}
.hero-slides li{position:absolute; inset:0; list-style:none; opacity:0;}
/* Without JS the extra slides never get a src, so show only the first and
   never run the cycle. The `js` class is set by an inline script in <head>. */
.hero-slides li:first-child{opacity:1;}
/* No will-change here on purpose: it would promote four full-viewport images
   to permanent compositing layers for a fade that runs once every 24s. */
.js .hero-slides li{animation:heroFade 24s infinite;}
.js .hero-slides li:first-child{opacity:0;}
/* Delays are shifted back by the 0.72s fade-in so the first slide is already
   opaque at t=0. Starting at 0s leaves the hero black for the first frame. */
.js .hero-slides li:nth-child(1){animation-delay:-.75s;}
.js .hero-slides li:nth-child(2){animation-delay:5.25s;}
.js .hero-slides li:nth-child(3){animation-delay:11.25s;}
.js .hero-slides li:nth-child(4){animation-delay:17.25s;}
.hero-slides img{width:100%; height:100%; object-fit:cover; filter:brightness(.78) saturate(1.06);}
@keyframes heroFade{0%{opacity:0}3%{opacity:1}25%{opacity:1}28%{opacity:0}100%{opacity:0}}
.hero::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  /* many closely spaced stops, otherwise the ramp bands */
  background:linear-gradient(180deg,
    rgba(10,10,12,.52) 0%, rgba(10,10,12,.34) 8%, rgba(10,10,12,.16) 18%,
    rgba(10,10,12,.05) 30%, rgba(10,10,12,.05) 44%, rgba(10,10,12,.22) 62%,
    rgba(10,10,12,.48) 78%, rgba(10,10,12,.72) 90%, rgba(10,10,12,.84) 100%);
}
.hero-body{
  position:absolute; z-index:3; left:var(--edge); right:var(--edge);
  bottom:clamp(28px,6vh,60px);
}
.hero h1{
  /* sized against viewport height too, so it can't collide with the header
     on a short-but-wide window like 1280x720 */
  font-size:clamp(38px,min(7.4vw,12vh),100px);
  margin:12px 0 18px; max-width:15ch;
  text-shadow:0 2px 40px rgba(0,0,0,.45);
}
.hero-row{display:flex; align-items:flex-end; justify-content:space-between; gap:24px; flex-wrap:wrap;}
.hero-row p{margin:0; max-width:38ch; color:var(--fg-dim); font-size:clamp(14px,1.4vw,15.5px);}
.hero-bar{width:min(240px,40vw); height:1px; background:rgba(255,255,255,.26); overflow:hidden; margin-bottom:12px;}
.hero-bar i{display:block; height:100%; background:#fff; animation:heroTrack 6s linear infinite;}
@keyframes heroTrack{from{width:0}to{width:100%}}

/* Must come after the .js rules above so it wins on order, not specificity. */
@media (prefers-reduced-motion:reduce){
  .js .hero-slides li, .hero-slides li{animation:none; opacity:0;}
  .js .hero-slides li:first-child, .hero-slides li:first-child{opacity:1;}
  .hero-bar{display:none;}
  .tile img, .svc img, .shot img{transition:none;}
}

/* ── mosaic ─────────────────────────────────────────────────────────────── */
.mosaic{
  display:grid; gap:var(--gap);
  /* minmax(0,1fr), not 1fr. A bare 1fr floors each track at min-content, so
     the text panel's longest word widens its column and squeezes the photos. */
  grid-template-columns:repeat(4,minmax(0,1fr));
  grid-auto-rows:clamp(132px,15.5vw,290px);
}
.tile{position:relative; overflow:hidden; background:var(--stone-2);}
.tile img{width:100%; height:100%; object-fit:cover; transition:transform .9s var(--ease);}
.tile:hover img{transform:scale(1.055);}
.tile figcaption{
  position:absolute; inset:auto 0 0 0; padding:26px 16px 13px;
  background:linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.28) 45%, rgba(0,0,0,.74) 100%);
  font-size:12.5px; letter-spacing:.05em;
  opacity:0; transform:translateY(6px); transition:opacity .3s, transform .3s;
}
.tile:hover figcaption, .tile:focus-within figcaption{opacity:1; transform:none;}
.t-2w{grid-column:span 2;}
.t-2h{grid-row:span 2;}

.panel{
  background:var(--stone-2); padding:clamp(22px,2.6vw,38px);
  display:flex; flex-direction:column; justify-content:center;
}
.panel h2{font-size:clamp(23px,2.7vw,38px); margin:12px 0 12px; line-height:1.04;}
.panel p{margin:0 0 18px; color:var(--fg-dim); font-size:14.5px;}
.link-u{
  font-size:12px; letter-spacing:.16em; text-transform:uppercase;
  border-bottom:1px solid rgba(255,255,255,.4); padding-bottom:4px;
  align-self:flex-start; min-height:44px; display:inline-flex; align-items:center;
}
.link-u:hover{border-color:#fff;}

/* ── services strip ─────────────────────────────────────────────────────── */
.strip{display:grid; gap:var(--gap); grid-template-columns:repeat(4,minmax(0,1fr));}
.svc{position:relative; aspect-ratio:3/4; overflow:hidden; background:var(--stone-2); display:block;}
.svc img{width:100%; height:100%; object-fit:cover; filter:brightness(.58);
  transition:transform .9s var(--ease), filter .5s;}
.svc:hover img{transform:scale(1.05); filter:brightness(.42);}
.svc-body{position:absolute; inset:auto 0 0 0; padding:clamp(18px,2vw,28px);}
.svc h3{font-size:clamp(18px,1.9vw,25px); font-weight:300; margin:10px 0 8px; letter-spacing:-.015em;}
.svc p{margin:0; font-size:13.5px; color:var(--fg-dim); line-height:1.55;}

/* ── about panel over a photo ───────────────────────────────────────────── */
.about{position:relative; overflow:hidden; min-height:clamp(460px,60vw,700px);}
.about > img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; filter:brightness(.5);}
.about-panel{
  position:relative; z-index:2; margin-left:auto;
  width:min(560px,100%); margin-right:clamp(16px,4vw,60px);
  top:clamp(44px,7vw,88px);
  background:rgba(16,16,19,.62);
  -webkit-backdrop-filter:blur(22px) saturate(1.2);
  backdrop-filter:blur(22px) saturate(1.2);
  border:1px solid rgba(255,255,255,.11);
  padding:clamp(26px,3.4vw,46px);
}
.about-panel h2{font-size:clamp(27px,3.3vw,44px); margin:12px 0 18px;}
.about-panel p{color:rgba(255,255,255,.82); font-size:15px;}
.quote{
  border-left:1px solid rgba(255,255,255,.35); padding-left:18px;
  margin-top:24px; color:#fff; font-size:16px; font-weight:300; line-height:1.5;
}

/* ── reviews ────────────────────────────────────────────────────────────── */
.reviews-wrap{background:var(--stone-2);}
.reviews{display:grid; grid-template-columns:repeat(auto-fit,minmax(250px,1fr));
  gap:clamp(20px,3vw,40px); margin-top:30px;}
.review p{margin:0 0 14px; font-size:15px; color:rgba(255,255,255,.55); font-style:italic;}
.review .who{font-size:11px; letter-spacing:.18em; text-transform:uppercase; color:var(--fg-dim);}
.slot-note{margin-top:26px; font-size:11px; letter-spacing:.14em;
  text-transform:uppercase; color:rgba(255,255,255,.55);}

/* ── cta ────────────────────────────────────────────────────────────────── */
.cta{position:relative; overflow:hidden; text-align:center;
  padding:clamp(76px,11vw,156px) var(--edge);}
.cta > img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  filter:brightness(.36) saturate(1.05);}
.cta-in{position:relative; z-index:2;}
.cta h2{font-size:clamp(30px,5vw,68px); margin:14px 0 14px;}
.cta p{color:var(--fg-dim); max-width:46ch; margin:0 auto 32px;}

/* ── footer ─────────────────────────────────────────────────────────────── */
.site-footer{
  display:flex; justify-content:space-between; align-items:center;
  gap:18px 26px; flex-wrap:wrap;
  padding:28px var(--edge); font-size:13px; color:var(--fg-dim);
  border-top:1px solid rgba(255,255,255,.09);
}
.site-footer a{min-height:44px; display:inline-flex; align-items:center;}
.site-footer a:hover{color:#fff;}
.f-contact{display:flex; gap:20px 26px; flex-wrap:wrap; align-items:center;}

/* ── portfolio ──────────────────────────────────────────────────────────── */
/* No bar and no rule: the container is transparent and the chips float over
   whatever is scrolling past. It stays sticky because 56 photographs is a
   long way to scroll back up to change category. */
.gallery-nav{
  position:sticky; top:var(--header-h); z-index:70;
  display:flex; gap:6px; flex-wrap:wrap;
  padding:12px var(--edge);
}
/* The backing moved off the bar and onto each chip. Without it the labels
   sit directly on photographs as they scroll under, and white-on-white is
   unreadable the moment a bright frame passes behind. */
.gallery-nav a{
  display:inline-flex; align-items:center; min-height:44px; padding:0 18px;
  border-radius:999px; font-size:12px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--fg-dim); border:1px solid rgba(255,255,255,.16);
  background:rgba(14,14,17,.66);
  -webkit-backdrop-filter:blur(12px) saturate(1.2);
  backdrop-filter:blur(12px) saturate(1.2);
  transition:color .2s, border-color .2s, background .2s;
}
.gallery-nav a:hover{color:#fff; border-color:rgba(255,255,255,.5);
  background:rgba(20,20,24,.82);}

.cat{padding-block:clamp(44px,6vw,86px); scroll-margin-top:calc(var(--header-h) + 68px);}
.cat-head{padding-inline:var(--edge); margin-bottom:clamp(20px,2.6vw,34px);}
.cat-head h2{font-size:clamp(26px,3.6vw,48px); margin:10px 0 0;}
.cat-head p{margin:12px 0 0; color:var(--fg-dim); font-size:15px;}

/* Capped, or columns reach 700px+ on a 2560 monitor and a 4:5 portrait
   renders taller than the viewport. */
.grid{
  max-width:var(--maxw-wide); margin-inline:auto;
  padding-inline:var(--edge);
  display:grid; gap:clamp(8px,1vw,14px);
  grid-template-columns:repeat(auto-fill,minmax(min(100%,300px),1fr));
}
.shot{
  position:relative; display:block; width:100%; padding:0; overflow:hidden;
  background:var(--stone-2); border:0; line-height:0;
}
.shot img{width:100%; height:100%; object-fit:cover; transition:transform .8s var(--ease), opacity .5s;}
.shot:hover img{transform:scale(1.045);}
.shot::after{
  content:""; position:absolute; inset:0;
  background:rgba(0,0,0,.16); opacity:0; transition:opacity .3s;
}
.shot:hover::after{opacity:1;}

/* ── lightbox ───────────────────────────────────────────────────────────── */
.lb{
  position:fixed; inset:0; z-index:200;
  background:rgba(8,8,10,.96);
  -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px);
  /* explicit rows, or a child's max-height:100% has no definite height to
     resolve against and a tall image runs off the screen */
  display:grid; grid-template-rows:auto minmax(0,1fr) auto;
  opacity:0; visibility:hidden;
  transition:opacity .25s, visibility 0s linear .25s;
}
.lb.open{opacity:1; visibility:visible; transition:opacity .25s, visibility 0s;}
.lb[hidden]{display:none;}
.lb-top{display:flex; justify-content:space-between; align-items:center;
  padding:12px var(--edge); font-size:12px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--fg-dim);}
.lb-stage{
  position:relative; height:100%; min-height:0;  /* min-height:0 lets it shrink */
  display:flex; align-items:center; justify-content:center;
  padding:0 clamp(8px,4vw,72px);
}
.lb-stage img{
  max-width:100%; max-height:100%; width:auto; height:auto;
  object-fit:contain;
}
.lb-bottom{padding:14px var(--edge) 22px; text-align:center;
  font-size:13.5px; color:var(--fg-dim); min-height:56px;}
.lb-btn{
  position:absolute; top:50%; transform:translateY(-50%);
  width:52px; height:52px; border-radius:999px;
  display:flex; align-items:center; justify-content:center;
  background:rgba(255,255,255,.1); border:1px solid rgba(255,255,255,.2);
  transition:background .2s;
}
.lb-btn:hover{background:rgba(255,255,255,.22);}
.lb-prev{left:clamp(6px,1.6vw,22px);}
.lb-next{right:clamp(6px,1.6vw,22px);}
.lb-close{
  width:44px; height:44px; border-radius:999px;
  display:inline-flex; align-items:center; justify-content:center;
  background:rgba(255,255,255,.1); border:1px solid rgba(255,255,255,.2);
}
.lb-close:hover{background:rgba(255,255,255,.22);}
.lb svg{width:18px; height:18px;}
body.lb-open{overflow:hidden;}

/* ── services page ──────────────────────────────────────────────────────── */
.svc-row{
  display:grid; grid-template-columns:1fr 1fr; gap:clamp(20px,3.6vw,56px);
  align-items:center; padding-block:clamp(34px,4.6vw,72px);
  border-top:1px solid var(--stone-3);
}
.svc-row:first-of-type{border-top:0;}
.svc-row.flip .svc-media{order:2;}
.svc-media{aspect-ratio:4/3; overflow:hidden; background:var(--stone-2);}
.svc-media img{width:100%; height:100%; object-fit:cover;}
.svc-row h2{font-size:clamp(24px,3vw,38px); margin:12px 0 14px;}
.svc-row p{color:var(--fg-dim); font-size:15.5px;}
.svc-row ul{margin:18px 0 0; padding:0; list-style:none;}
.svc-row li{
  position:relative; padding-left:20px; margin-bottom:9px;
  font-size:14.5px; color:var(--fg-dim);
}
.svc-row li::before{
  content:""; position:absolute; left:0; top:.62em;
  width:7px; height:1px; background:var(--fg-faint);
}

/* ── prose ──────────────────────────────────────────────────────────────── */
.prose{max-width:64ch;}
.prose p{color:rgba(255,255,255,.82); font-size:16.5px; line-height:1.68;}
.prose h2{font-size:clamp(22px,2.4vw,30px); margin:38px 0 14px;}

/* ── forms ──────────────────────────────────────────────────────────────── */
.form{display:grid; gap:18px; max-width:620px;}
.field{display:grid; gap:7px;}
.field label{font-size:12px; letter-spacing:.14em; text-transform:uppercase; color:var(--fg-dim);}
.field .req{color:rgba(255,255,255,.45);}
.field input, .field select, .field textarea{
  font:inherit; font-size:15px; color:var(--fg);
  background:var(--stone-2); border:1px solid var(--stone-3);
  padding:13px 14px; min-height:48px; width:100%;
  border-radius:var(--radius); transition:border-color .2s;
}
.field textarea{min-height:130px; resize:vertical;}
.field input:focus, .field select:focus, .field textarea:focus{
  outline:none; border-color:rgba(255,255,255,.62);
}
.field input::placeholder, .field textarea::placeholder{color:rgba(255,255,255,.34);}
.field select{appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--fg-dim) 50%),
                   linear-gradient(135deg,var(--fg-dim) 50%,transparent 50%);
  background-position:calc(100% - 20px) 21px, calc(100% - 14px) 21px;
  background-size:6px 6px, 6px 6px; background-repeat:no-repeat;}
.field-row{display:grid; grid-template-columns:1fr 1fr; gap:18px;}
/* Honeypot. Positioned off-screen rather than display:none, because some
   bots skip hidden inputs and fill everything else. */
.hp{position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden;}
.form-note{font-size:13px; color:var(--fg-faint); margin:0;}
.form-status{font-size:14.5px; margin:0; min-height:1.5em;}
.form-status.err{color:#ffb4a8;}
.form-status.ok{color:#a8e6bd;}

.contact-grid{display:grid; grid-template-columns:1.25fr .75fr; gap:clamp(26px,5vw,72px);}
.contact-detail{display:grid; gap:22px; align-content:start;}
.contact-detail .k{font-size:11px; letter-spacing:.2em; text-transform:uppercase; color:var(--fg-faint);}
.contact-detail .v{font-size:16px; margin-top:5px;}
.contact-detail .v a{display:inline-flex; align-items:center; min-height:44px;}
.contact-detail a:hover{color:#fff; text-decoration:underline;}

/* ── 404 ────────────────────────────────────────────────────────────────── */
.err-page{
  position:relative; min-height:100svh; display:grid; place-items:center;
  text-align:center; padding:var(--edge); overflow:hidden;
}
.err-page > img{position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; filter:brightness(.34);}
.err-in{position:relative; z-index:2;}
.err-in h1{font-size:clamp(40px,8vw,96px); margin:14px 0 16px;}

/* The text panel is a grid item stretched to grid-auto-rows, so its height is
   whatever a photo tile's row is: clamp(132px,15.5vw,290px). Its own text
   needs roughly 175-195px depending on how narrowly it wraps, and one row is
   only that tall once 15.5vw gets there, around 1280px wide.

   Below that the panel centres more content than it has room for, so the
   overflow splits top and bottom and the eyebrow escapes above the panel:
   on a phone it slides under the hero photo and looks cut in half. It broke
   at every width tested from 320px to 1100px, laptops included.

   Spanning two rows is the fix rather than letting rows auto-size. Changing
   grid-auto-rows to minmax(...,auto) makes every row size to its content,
   and a tile's content is a full-resolution photograph: tiles jumped to
   342-710px tall and the mosaic doubled in height.
   ── responsive ─────────────────────────────────────────────────────────── */
@media (max-width:1280px){
  .panel{grid-row:span 2;}
}
@media (max-width:900px){
  .mosaic{grid-template-columns:repeat(2,minmax(0,1fr));}
  .strip{grid-template-columns:repeat(2,minmax(0,1fr));}
  .panel{grid-column:span 2;}
  .svc-row, .contact-grid{grid-template-columns:1fr;}
  .svc-row.flip .svc-media{order:0;}
  .about{min-height:0;}
  .about > img{position:relative; height:clamp(220px,44vw,340px);}
  .about-panel{width:100%; margin:0; top:0; border-left:0; border-right:0;}
}
@media (max-width:560px){
  .field-row{grid-template-columns:1fr;}
  .site-footer{flex-direction:column; align-items:flex-start; gap:6px;}
  .lb-btn{width:44px; height:44px;}
}
