/* =========================================================
   JK Photography — page styles
   Everything here is specific to this site's pages. The shared
   chrome (nav, footer, buttons, portal, admin) lives in site.css
   and is driven by the four brand tokens at the top of that file.

   Layout principle: nothing is centered by default and nothing is a
   row of three equal cards. Sections alternate an asymmetric split,
   a full-bleed band, and an offset editorial stack, so scrolling the
   page has a rhythm rather than a repeated shape.
   ========================================================= */

:root{
  --jk-arch:clamp(120px,17vw,240px);   /* the half-arch corner radius */
  --jk-gut:clamp(20px,5vw,52px);
}

/* ---------------------------------------------------------------- shared */
.jk-kicker{display:flex;align-items:center;gap:14px;margin-bottom:20px;}
.jk-kicker i{display:block;height:1px;width:44px;background:var(--cyan);flex:none;}
.jk-kicker span{font-size:.66rem;letter-spacing:.26em;text-transform:uppercase;
  color:var(--cyan-deep);font-weight:500;}
.jk-kicker--center{justify-content:center;}

/* Two-column splits. A class rather than an inline grid-template-columns,
   because an inline style cannot be overridden by the mobile media query —
   the FAQ page stayed two columns at 390px and pushed its sidebar off the
   screen, which is exactly the failure this class exists to prevent. */
.jk-2col{display:grid;gap:clamp(30px,5vw,72px);align-items:start;
  grid-template-columns:minmax(0,1.35fr) minmax(0,.65fr);}
.jk-2col--even{grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr);align-items:center;}

.jk-lede{font-size:1.02rem;line-height:1.8;color:var(--ink-soft);font-weight:300;max-width:56ch;}
.jk-lede p + p{margin-top:1.1em;}

.jk-link{position:relative;display:inline-flex;align-items:center;gap:.6em;
  font-size:.74rem;letter-spacing:.16em;text-transform:uppercase;font-weight:500;
  color:var(--ink);padding-bottom:5px;}
.jk-link::after{content:"";position:absolute;left:0;bottom:0;height:1px;width:100%;
  background:var(--ink);transform:scaleX(0);transform-origin:right;
  transition:transform .55s var(--ease-out);}
.jk-link:hover::after{transform:scaleX(1);transform-origin:left;}
.jk-link svg{transition:transform .5s var(--ease-out);}
.jk-link:hover svg{transform:translateX(6px);}

/* A photograph in a frame that reveals itself. The clip is the reveal; the
   scale settle underneath it stops the picture from looking pasted on. */
.jk-fig{position:relative;overflow:hidden;background:var(--cream-2);}
.jk-fig img{width:100%;height:100%;object-fit:cover;display:block;}
/* cimg_set()/img_set() add width and height ATTRIBUTES so the browser can
   reserve the right shape before the picture lands. Those are presentational
   hints, which means the element's height is no longer `auto` — and CSS
   `aspect-ratio` is only consulted when one axis is auto. So anywhere the
   layout (rather than the frame around it) sets the shape, the image has to
   hand its height back. Without this the hero's print card rendered 1301px
   tall instead of 225px, on a rule that looked perfectly correct. */
.jkprint img,
.jkgal .jk-fig img{height:auto;}
.jk-fig--arch{border-radius:var(--jk-arch) 0 0 0;}
.jk-fig--dome{border-radius:var(--jk-arch) var(--jk-arch) 0 0;}
.jk-fig--soft{border-radius:var(--radius-card);}

/* ---------------------------------------------------------------- hero */
.jkhero{position:relative;display:grid;grid-template-columns:46fr 54fr;
  grid-template-areas:"top media" "bot media";align-content:center;
  min-height:100svh;background:var(--paper);}
.jkhero__top{grid-area:top;padding:clamp(104px,13vh,132px) 4vw 0 var(--jk-gut);position:relative;z-index:5;}
.jkhero__bot{grid-area:bot;padding:0 4vw clamp(40px,6vh,64px) var(--jk-gut);position:relative;z-index:5;}

.jkhero h1{font-size:clamp(2.4rem,1.2rem + 3.2vw,4.4rem);line-height:1.04;margin-bottom:20px;}
.jkhero h1 .serif{display:inline;}
.jkhero__sub{max-width:38ch;font-size:.98rem;line-height:1.78;color:var(--ink-soft);font-weight:300;}

.jkhero__cta{display:flex;align-items:center;gap:26px;margin-top:28px;flex-wrap:wrap;}

.jkhero__meta{display:flex;max-width:440px;margin-top:34px;
  border-top:1px solid var(--line);padding-top:17px;}
.jkhero__meta div{padding-right:26px;margin-right:26px;border-right:1px solid var(--line);}
.jkhero__meta div:last-child{border:none;padding-right:0;margin-right:0;}
.jkhero__meta b{display:block;font-family:var(--font-accent);font-size:1.55rem;
  font-weight:400;line-height:1;color:var(--ink);}
.jkhero__meta small{display:block;margin-top:6px;font-size:.58rem;letter-spacing:.16em;
  text-transform:uppercase;color:var(--cyan-deep);}

.jkhero__media{grid-area:media;position:relative;align-self:stretch;
  min-height:calc(100svh - 120px);margin-top:120px;}
.jkhero__media .jk-fig{position:absolute;inset:0;}
.jkhero__media .jk-fig::after{content:"";position:absolute;inset:0;
  background:linear-gradient(102deg,rgba(250,247,242,.34) 0%,rgba(250,247,242,0) 26%);}

/* the offset print, sitting across the seam between the two columns */
.jkprint{position:absolute;left:-74px;bottom:15vh;width:200px;z-index:6;
  background:#fff;padding:10px 10px 40px;
  box-shadow:0 26px 60px -20px rgba(34,32,29,.34);
  transform:rotate(-2.6deg);
  transition:transform .7s var(--ease-out),box-shadow .7s var(--ease-out);}
.jkprint img{width:100%;aspect-ratio:4/5;object-fit:cover;display:block;}
.jkprint figcaption{position:absolute;left:0;right:0;bottom:13px;text-align:center;
  font-family:var(--font-head);font-style:italic;font-size:.82rem;color:var(--ink-soft);}
.jkprint:hover{transform:rotate(0) translateY(-8px) scale(1.03);
  box-shadow:0 34px 70px -18px rgba(34,32,29,.42);}

.jkcue{position:absolute;left:var(--jk-gut);bottom:26px;display:flex;align-items:center;gap:12px;
  z-index:5;font-size:.56rem;letter-spacing:.28em;text-transform:uppercase;color:var(--cyan-deep);}
.jkcue i{position:relative;display:block;width:56px;height:1px;background:var(--line);overflow:hidden;}
.jkcue i::after{content:"";position:absolute;inset:0;background:var(--cyan);
  transform:translateX(-100%);animation:jkSweep 2.4s var(--ease-out) infinite;}
@keyframes jkSweep{50%{transform:translateX(0)}100%{transform:translateX(100%)}}

/* ---------------------------------------------------------------- marquee */
.jkband{border-block:1px solid var(--line);background:var(--cream);
  padding:15px 0;overflow:hidden;white-space:nowrap;}
.jkband__track{display:flex;width:max-content;animation:jkMarq 38s linear infinite;
  will-change:transform;}
.jkband__set{display:flex;flex:none;gap:48px;padding-right:48px;}
.jkband:hover .jkband__track{animation-play-state:paused;}
.jkband span{font-family:var(--font-head);font-style:italic;font-size:1.12rem;
  color:var(--cyan-deep);display:inline-flex;align-items:center;gap:48px;}
.jkband span::after{content:"✦";font-style:normal;font-size:.5rem;color:var(--cyan);}
@keyframes jkMarq{to{transform:translateX(-25%)}}

/* ---------------------------------------------------------------- philosophy */
.jkphil{background:var(--paper);padding-block:clamp(64px,9vw,128px);}
.jkphil__grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);
  gap:clamp(32px,6vw,88px);align-items:start;}
.jkphil__pull{font-family:var(--font-accent);font-weight:300;
  font-size:clamp(1.7rem,1rem + 2.3vw,3.1rem);line-height:1.14;letter-spacing:-.012em;
  color:var(--ink);}
.jkphil__pull em{font-style:italic;color:var(--cyan-deep);}
.jkphil__body{padding-top:.6rem;}
.jkphil__sign{margin-top:26px;display:flex;align-items:center;gap:14px;}
.jkphil__sign i{display:block;height:1px;width:38px;background:var(--cyan);}
.jkphil__sign span{font-family:var(--font-head);font-style:italic;font-size:1.02rem;color:var(--ink-soft);}

/* ---------------------------------------------------------------- services */
.jkserv{background:var(--cream);padding-block:clamp(56px,8vw,112px);}
.jkserv__row{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,5vw,72px);
  align-items:center;padding-block:clamp(28px,4vw,52px);}
.jkserv__row + .jkserv__row{border-top:1px solid var(--line);}
.jkserv__row:nth-child(even) .jkserv__media{order:2;}
.jkserv__media .jk-fig{aspect-ratio:5/4;}
.jkserv__row:nth-child(odd) .jk-fig{border-radius:var(--jk-arch) 0 0 0;}
.jkserv__row:nth-child(even) .jk-fig{border-radius:0 var(--jk-arch) 0 0;}
.jkserv__n{font-family:var(--font-accent);font-size:.9rem;color:var(--cyan);
  letter-spacing:.1em;display:block;margin-bottom:10px;}
.jkserv h3{font-family:var(--font-accent);font-weight:300;
  font-size:clamp(1.6rem,1.1rem + 1.5vw,2.5rem);line-height:1.08;margin-bottom:14px;color:var(--ink);}
.jkserv p{font-size:.95rem;line-height:1.78;color:var(--ink-soft);font-weight:300;max-width:46ch;}
.jkserv .jk-link{margin-top:20px;}

/* ---------------------------------------------------------------- work strip */
.jkstrip{background:var(--paper);padding-block:clamp(56px,8vw,104px);overflow:hidden;}
.jkstrip__head{display:flex;justify-content:space-between;align-items:flex-end;
  gap:24px;flex-wrap:wrap;margin-bottom:34px;}
.jkstrip__head h2{font-size:clamp(1.8rem,1.2rem + 2vw,3rem);}
.jkstrip__rail{display:flex;gap:clamp(12px,1.6vw,22px);overflow-x:auto;
  scroll-snap-type:x mandatory;
  scrollbar-width:none;-ms-overflow-style:none;cursor:grab;
  /* The cards rise 6px on hover, and the rail had no headroom: overflow-x:auto
     makes overflow-y:auto too, so the top 6px of the photograph was cut off at
     the exact moment somebody pointed at it. Padding gives the lift somewhere
     to go; the negative margin means nothing else on the page moves. */
  padding-block:12px 14px;
  margin-block:-12px 0;}
.jkstrip__rail::-webkit-scrollbar{display:none;}
.jkstrip__rail.is-drag{cursor:grabbing;scroll-snap-type:none;}
.jkstrip__rail > a{flex:0 0 clamp(210px,24vw,320px);scroll-snap-align:start;}
.jkstrip__rail .jk-fig{aspect-ratio:4/5;border-radius:var(--radius-card);
  transition:transform .6s var(--ease-out);}
.jkstrip__rail > a:nth-child(3n+2) .jk-fig{aspect-ratio:4/5;margin-top:clamp(14px,2.4vw,38px);}
.jkstrip__rail > a:hover .jk-fig{transform:translateY(-6px);}
.jkstrip__rail .jk-fig img{transition:transform 1.1s var(--ease-out);}
.jkstrip__rail > a:hover .jk-fig img{transform:scale(1.05);}

/* ---------------------------------------------------------------- why us */
.jkwhy{background:var(--navy);color:var(--ink-invert);padding-block:clamp(56px,8vw,112px);}
.jkwhy__grid{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(30px,6vw,80px);align-items:center;}
.jkwhy h2{color:var(--ink-invert);font-size:clamp(1.9rem,1.2rem + 2.2vw,3.2rem);margin-bottom:26px;}
.jkwhy .jk-kicker span{color:var(--cyan);}
.jkwhy .jk-kicker i{background:var(--cyan);}
.jkwhy__list{list-style:none;padding:0;margin:0;}
.jkwhy__list li{display:flex;gap:18px;padding:16px 0;border-top:1px solid rgba(250,247,242,.16);}
.jkwhy__list li:last-child{border-bottom:1px solid rgba(250,247,242,.16);}
.jkwhy__list b{font-family:var(--font-accent);font-weight:400;font-size:1.02rem;
  color:var(--cyan);flex:none;width:2ch;line-height:1.6;}
.jkwhy__list span{font-size:.95rem;line-height:1.68;color:rgba(250,247,242,.86);font-weight:300;}
.jkwhy__media .jk-fig{aspect-ratio:4/5;border-radius:var(--jk-arch) var(--jk-arch) 0 0;}

/* ---------------------------------------------------------------- quotes */
.jkquotes{background:var(--paper);padding-block:clamp(56px,8vw,104px);overflow:hidden;}
.jkquotes__rail{display:flex;gap:22px;overflow-x:auto;scroll-snap-type:x mandatory;
  scrollbar-width:none;
  /* Full-bleed. The rail lives inside .wrap, so it was boxed to 1240px and the
     cards stopped dead at an invisible edge instead of running off the side of
     the screen the way a scrollable rail should.

     Both values are the same expression: 50% resolves against .wrap's content
     box, 50vw against the viewport, so the difference is exactly the space
     .wrap leaves at each side. As a negative margin it cancels that space; as
     padding it puts it back inside the scroller — which is what keeps the
     first card lined up with the heading above it at every width. */
  margin-inline:calc(50% - 50vw);
  padding-inline:calc(50vw - 50%);
  /* Snapping ignores padding unless it is told not to: the mandatory snap put
     the first card's left edge against the scroller's edge, scrolling straight
     past the gutter the padding had just created, so the card sat flush to the
     window while the heading above it stayed indented.

     This cannot reuse the expression above. Percentages in scroll-padding
     resolve against the scroll container's own box — which is now the full
     viewport — so `50vw - 50%` computes to zero here and does nothing. Spelled
     out instead: the gutter is whatever .wrap leaves at the side, which is its
     own padding once the viewport is narrower than --maxw and half the leftover
     plus that padding once it is wider. */
  scroll-padding-inline:max(var(--jk-gut), calc((100vw - var(--maxw)) / 2 + var(--jk-gut)));
  /* overflow-x:auto forces overflow-y to auto as well, so the rail clips
     vertically whether or not anyone asked it to. The cards lift 5px on hover
     and cast a shadow; without headroom both were sliced off at the top. The
     negative margin keeps the section's spacing exactly as it was. */
  padding-block:20px 26px;
  margin-block:-20px -12px;}
.jkquotes__rail::-webkit-scrollbar{display:none;}
.jkquote{flex:0 0 min(420px,82vw);scroll-snap-align:start;background:var(--cream);
  border:1px solid var(--line);border-radius:var(--radius-card);
  padding:clamp(24px,3vw,38px);display:flex;flex-direction:column;
  transition:transform .5s var(--ease-out),box-shadow .5s var(--ease-out);}
.jkquote:hover{transform:translateY(-5px);box-shadow:var(--shadow-md);}
.jkquote__mark{font-family:var(--font-head);font-style:italic;font-size:2.6rem;
  line-height:.7;color:var(--cyan);margin-bottom:12px;}
.jkquote p{font-size:1rem;line-height:1.74;color:var(--ink-soft);font-weight:300;flex:1;}
.jkquote cite{display:block;margin-top:20px;font-style:normal;font-size:.62rem;
  letter-spacing:.18em;text-transform:uppercase;color:var(--cyan-deep);}

/* ---------------------------------------------------------------- films */
.jkfilms{background:var(--cream);padding-block:clamp(56px,8vw,104px);}
.jkfilmgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(320px,100%),1fr));
  gap:clamp(18px,2.4vw,34px);}
.jkfilm{display:block;}
.jkfilm .jk-fig{aspect-ratio:16/9;border-radius:var(--radius-card);}
.jkfilm .jk-fig img{transition:transform 1.1s var(--ease-out);}
.jkfilm:hover .jk-fig img{transform:scale(1.05);}
.jkfilm__play{position:absolute;inset:0;display:grid;place-items:center;z-index:2;}
.jkfilm__play span{width:62px;height:62px;border-radius:50%;display:grid;place-items:center;
  background:rgba(250,247,242,.9);color:var(--ink);
  transition:transform .5s var(--ease-out),background .4s;}
.jkfilm:hover .jkfilm__play span{transform:scale(1.1);background:#fff;}
.jkfilm__meta{display:flex;justify-content:space-between;gap:12px;align-items:baseline;margin-top:14px;}
.jkfilm__meta h3{font-family:var(--font-accent);font-weight:300;font-size:1.3rem;color:var(--ink);}
.jkfilm__meta time{font-size:.62rem;letter-spacing:.16em;text-transform:uppercase;color:var(--cyan-deep);}

/* lightbox */
.jklb{position:fixed;inset:0;z-index:400;background:rgba(20,18,16,.94);
  display:none;place-items:center;padding:clamp(14px,4vw,48px);}
.jklb.is-open{display:grid;}
.jklb__inner{width:min(1100px,100%);aspect-ratio:16/9;position:relative;}
.jklb iframe{position:absolute;inset:0;width:100%;height:100%;border:0;border-radius:var(--radius);}
.jklb__close{position:absolute;top:-46px;right:0;width:38px;height:38px;border-radius:50%;
  border:1px solid rgba(250,247,242,.3);background:none;color:#faf7f2;display:grid;place-items:center;
  transition:background .3s,transform .3s;}
.jklb__close:hover{background:rgba(250,247,242,.12);transform:rotate(90deg);}
.jklb__note{position:absolute;left:0;right:0;top:calc(50% + 4rem);text-align:center;
  color:rgba(250,247,242,.7);font-size:.9rem;}

/* ---------------------------------------------------------------- closing CTA */
.jkcta{position:relative;isolation:isolate;padding-block:clamp(72px,11vw,148px);
  text-align:center;color:var(--ink-invert);overflow:hidden;}
.jkcta__bg{position:absolute;inset:0;z-index:-2;}
.jkcta__bg img{width:100%;height:100%;object-fit:cover;}
.jkcta::after{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(180deg,rgba(20,18,16,.55),rgba(20,18,16,.78));}
.jkcta h2{color:var(--ink-invert);font-size:clamp(2rem,1.2rem + 2.6vw,3.6rem);margin-bottom:18px;}
.jkcta p{color:rgba(250,247,242,.84);font-size:1rem;line-height:1.7;font-weight:300;
  max-width:46ch;margin-inline:auto;}
.jkcta .jk-kicker{justify-content:center;}
.jkcta .jk-kicker span{color:var(--cyan);}
.jkcta .jk-kicker i{background:var(--cyan);}
.jkcta__btns{display:flex;gap:16px;justify-content:center;flex-wrap:wrap;margin-top:30px;}

/* ---------------------------------------------------------------- page heads */
.jkhead{position:relative;padding-top:clamp(112px,15vh,168px);padding-bottom:clamp(34px,5vw,60px);
  background:var(--paper);}
.jkhead h1{font-size:clamp(2.2rem,1.3rem + 3vw,4rem);line-height:1.04;margin-bottom:18px;max-width:18ch;}
.jkhead__sub{max-width:56ch;}
.jkhead--split{display:grid;grid-template-columns:1.1fr .9fr;gap:clamp(28px,5vw,72px);align-items:end;}

/* ---------------------------------------------------------------- gallery */
.jkgal{columns:3;column-gap:clamp(10px,1.4vw,20px);}
.jkgal > button{break-inside:avoid;margin-bottom:clamp(10px,1.4vw,20px);display:block;
  width:100%;padding:0;border:0;background:none;cursor:zoom-in;}
/* `display:block` above outranks the user-agent's [hidden]{display:none}, so
   the batching script's b.hidden = true had no effect and all 69 photographs
   stayed in the layout — a 35,000px page on a phone. */
.jkgal > button[hidden]{display:none;}
.jkgal .jk-fig{border-radius:var(--radius-sm);}
.jkgal .jk-fig img{transition:transform 1s var(--ease-out),opacity .4s;
  --zx:var(--gz,1);object-position:var(--px,50%) var(--py,50%);transform-origin:var(--px,50%) var(--py,50%);transform:scale(var(--zx));}
/* Phones have their own framing from the editor's Reframe (gal_pos_m), and
   fall back to the computer's when there is none. */
@media (max-width:900px){.jkgal .jk-fig img{--zx:var(--gzm,var(--gz,1));object-position:var(--pxm,var(--px,50%)) var(--pym,var(--py,50%));transform-origin:var(--pxm,var(--px,50%)) var(--pym,var(--py,50%));}}
.jkgal > button:hover .jk-fig img{transform:scale(calc(var(--zx) * 1.04));}
.jkgal > button:focus-visible{outline:2px solid var(--cyan);outline-offset:4px;}

/* image lightbox */
.jkilb{position:fixed;inset:0;z-index:400;background:rgba(20,18,16,.95);
  display:none;place-items:center;padding:clamp(12px,3vw,40px);}
.jkilb.is-open{display:grid;}
.jkilb img{max-width:100%;max-height:86svh;object-fit:contain;border-radius:var(--radius-sm);}
.jkilb__close,.jkilb__nav{position:absolute;width:44px;height:44px;border-radius:50%;
  border:1px solid rgba(250,247,242,.28);background:rgba(20,18,16,.5);color:#faf7f2;
  display:grid;place-items:center;transition:background .3s,transform .3s;}
.jkilb__close{top:18px;right:18px;}
.jkilb__close:hover{background:rgba(250,247,242,.16);transform:rotate(90deg);}
.jkilb__nav{top:50%;translate:0 -50%;}
.jkilb__nav--prev{left:14px;}
.jkilb__nav--next{right:14px;}
.jkilb__nav:hover{background:rgba(250,247,242,.16);}
.jkilb__count{position:absolute;bottom:20px;left:50%;translate:-50% 0;
  font-size:.68rem;letter-spacing:.18em;color:rgba(250,247,242,.66);}

/* ---------------------------------------------------------------- pricing */
.jkpkgs{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(290px,100%),1fr));
  gap:clamp(16px,2vw,26px);align-items:stretch;}
.jkpkg{position:relative;display:flex;flex-direction:column;background:var(--paper);
  border:1px solid var(--line);border-radius:var(--radius-card);
  padding:clamp(24px,3vw,34px);
  transition:transform .5s var(--ease-out),box-shadow .5s var(--ease-out),border-color .4s;}
.jkpkg:hover{transform:translateY(-6px);box-shadow:var(--shadow-md);border-color:var(--cyan);}
.jkpkg--pop{border-color:var(--cyan);box-shadow:var(--shadow-sm);}
.jkpkg__tag{position:absolute;top:-11px;left:clamp(24px,3vw,34px);
  background:var(--cyan-deep);color:var(--paper);font-size:.54rem;letter-spacing:.18em;
  text-transform:uppercase;padding:5px 12px;border-radius:100px;}
.jkpkg h3{font-family:var(--font-accent);font-weight:300;font-size:1.72rem;
  color:var(--ink);margin-bottom:6px;}
.jkpkg__hours{font-size:.62rem;letter-spacing:.16em;text-transform:uppercase;color:var(--cyan-deep);}
.jkpkg__price{font-family:var(--font-accent);font-weight:300;font-size:2.6rem;
  color:var(--ink);line-height:1;margin:18px 0 4px;}
.jkpkg__return{font-size:.76rem;color:var(--muted);}
.jkpkg ul{list-style:none;padding:0;margin:20px 0 0;flex:1;}
.jkpkg li{position:relative;padding:8px 0 8px 26px;font-size:.9rem;line-height:1.6;
  color:var(--ink-soft);font-weight:300;border-top:1px solid var(--line);}
.jkpkg li:first-child{border-top:0;}
.jkpkg li::before{content:"";position:absolute;left:2px;top:1.15em;width:9px;height:1px;
  background:var(--cyan);}
.jkpkg .btn{margin-top:24px;justify-content:center;}

/* The four reassurance lines under the Investments headline. A stacked
   label + note, four across, rather than a definition list — the dl put the
   note beside the term and both wrapped into a ragged second row. */
.jkassure{list-style:none;padding:0;margin:0;display:grid;
  grid-template-columns:repeat(auto-fit,minmax(min(190px,100%),1fr));
  gap:0 clamp(20px,3vw,40px);max-width:1000px;}
.jkassure li{padding:16px 0;border-top:1px solid var(--line);}
.jkassure b{display:block;font-family:var(--font-accent);font-weight:400;
  font-size:1.15rem;color:var(--ink);line-height:1.25;}
.jkassure span{display:block;margin-top:5px;font-size:.78rem;color:var(--muted);font-weight:300;}

.jkaddons{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(260px,100%),1fr));
  gap:0 clamp(20px,3vw,44px);}
.jkaddons div{display:flex;justify-content:space-between;gap:16px;align-items:baseline;
  padding:13px 0;border-bottom:1px solid var(--line);}
.jkaddons dt{font-size:.94rem;color:var(--ink);font-weight:300;}
.jkaddons dd{font-family:var(--font-accent);font-size:1.05rem;color:var(--cyan-deep);
  margin:0;white-space:nowrap;}

/* ---------------------------------------------------------------- FAQ */
.jkfaq{max-width:860px;}
.jkfaq details{border-bottom:1px solid var(--line);}
.jkfaq summary{list-style:none;cursor:pointer;padding:22px 44px 22px 0;position:relative;
  font-family:var(--font-accent);font-weight:300;font-size:clamp(1.12rem,1rem + .5vw,1.5rem);
  color:var(--ink);transition:color .3s;}
.jkfaq summary::-webkit-details-marker{display:none;}
.jkfaq summary:hover{color:var(--cyan-deep);}
.jkfaq summary::after{content:"";position:absolute;right:6px;top:50%;width:13px;height:1px;
  background:var(--ink);transition:transform .4s var(--ease-out);}
.jkfaq summary::before{content:"";position:absolute;right:12px;top:50%;width:1px;height:13px;
  margin-top:-6px;background:var(--ink);transform-origin:center;
  transition:transform .4s var(--ease-out),opacity .3s;}
.jkfaq details[open] summary::before{transform:rotate(90deg);opacity:0;}
.jkfaq__a{overflow:hidden;}
.jkfaq__a p{padding:0 0 24px;font-size:.96rem;line-height:1.8;color:var(--ink-soft);
  font-weight:300;max-width:64ch;}
.jkfaq details[open] .jkfaq__a{animation:jkOpen .45s var(--ease-out);}
@keyframes jkOpen{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}

/* ---------------------------------------------------------------- contact */
.jkcontact__grid{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(30px,5vw,72px);align-items:start;}
.jkcontact__aside .jk-fig{aspect-ratio:4/5;border-radius:var(--jk-arch) 0 0 0;margin-bottom:24px;}
.jkcontact__rows{list-style:none;padding:0;margin:0;}
.jkcontact__rows li{padding:14px 0;border-top:1px solid var(--line);}
.jkcontact__rows small{display:block;font-size:.58rem;letter-spacing:.18em;text-transform:uppercase;
  color:var(--cyan-deep);margin-bottom:5px;}
.jkcontact__rows a,.jkcontact__rows span{font-size:.98rem;color:var(--ink);font-weight:300;}
.jkcontact__rows a:hover{color:var(--cyan-deep);}

/* ---------------------------------------------------------------- about */
.jkabout__stack{display:grid;grid-template-columns:1fr 1fr;gap:clamp(12px,1.6vw,20px);}
.jkabout__stack .jk-fig:nth-child(1){aspect-ratio:3/4;border-radius:var(--jk-arch) 0 0 0;}
.jkabout__stack .jk-fig:nth-child(2){aspect-ratio:3/4;margin-top:clamp(24px,5vw,64px);border-radius:0 var(--jk-arch) 0 0;}

/* ---------------------------------------------------------------- responsive */
@media (max-width:900px){
  .jkhero{display:flex;flex-direction:column;grid-template-columns:none;
    grid-template-areas:none;min-height:0;padding:clamp(84px,13vh,104px) var(--jk-gut) 0;}
  .jkhero__top,.jkhero__bot{display:contents;}
  .jkhero .jk-kicker{order:1;}
  .jkhero h1{order:2;font-size:clamp(2.1rem,1.1rem + 4.6vw,2.9rem);}
  .jkhero__sub{order:3;max-width:none;}
  .jkhero__media{grid-area:auto;order:4;min-height:0;height:60svh;
    margin:30px calc(var(--jk-gut) * -1) 46px;width:auto;align-self:stretch;}
  .jkhero__media .jk-fig{border-radius:var(--jk-arch) var(--jk-arch) 0 0;}
  .jkhero__cta{order:5;margin-top:0;flex-direction:column;align-items:stretch;gap:14px;}
  .jkhero__cta .btn{width:100%;justify-content:center;}
  .jkhero__cta .jk-link{justify-content:center;}
  .jkhero__meta{order:6;max-width:none;margin:30px 0 40px;}
  .jkhero__meta div{padding-right:12px;margin-right:12px;flex:1;}
  .jkhero__meta b{font-size:1.25rem;}
  .jkhero__meta small{font-size:.5rem;letter-spacing:.1em;}
  .jkprint{left:auto;right:8px;bottom:-32px;width:112px;padding:7px 7px 27px;}
  .jkprint figcaption{font-size:.66rem;bottom:9px;}
  .jkcue{display:none;}

  .jkphil__grid,.jkserv__row,.jkwhy__grid,.jkcontact__grid,.jkhead--split,
  .jk-2col,.jk-2col--even{
    grid-template-columns:1fr;}
  .jkserv__row:nth-child(even) .jkserv__media{order:0;}
  .jkserv__row:nth-child(even) .jk-fig,.jkserv__row:nth-child(odd) .jk-fig{
    border-radius:var(--jk-arch) var(--jk-arch) 0 0;}
  .jkgal{columns:2;}
  .jkform{grid-template-columns:1fr;}
  .jkabout__stack .jk-fig:nth-child(2){margin-top:0;}
  .jkband span,.jkband__set{gap:30px;}
  .jkband__set{padding-right:30px;}
  .jkband span{font-size:.98rem;}
}
@media (max-width:520px){
  .jkgal{columns:1;}
  .jkstrip__rail > a{flex-basis:76vw;}
  .jkilb__nav{width:38px;height:38px;}
}

@media (prefers-reduced-motion:reduce){
  .jkband__track{animation:none;}
  .jkcue i::after{animation:none;transform:none;}
  .jkfaq details[open] .jkfaq__a{animation:none;}
  .jkprint,.jkstrip__rail .jk-fig,.jkquote{transition:none;}
}

/* =========================================================
   NAVIGATION — editorial masthead
   ---------------------------------------------------------
   The scaffold ships a dark frosted "pill" floating at the top center. It is
   a good app-shell shape and completely wrong for this site: a heavy navy
   lozenge sitting on an ivory page, in a design whose whole argument is
   restraint. This replaces it with a flush masthead — the monogram and
   wordmark at the left gutter, letterspaced links with a rule that draws on
   hover, and an outlined CTA that fills.

   All of it is a restyle: the markup, the hide-on-scroll behavior, the menu
   toggling and the focus handling are the scaffold's and still run. Only the
   appearance changes, which keeps this upgrade-safe.
   ========================================================= */

/* ---------------------------------------------------------------- desktop */
.navshell{
  /* site.js writes an inline `top` every scroll frame to float the pill clear
     of the announcement bar. Our masthead is flush, so this is the one place
     the cascade has to win against an inline style. */
  top:0 !important;
  padding-inline:0;
  transition:transform .55s var(--ease-out);
}
.navshell.hide{transform:translateY(-100%);}

.pillnav{
  width:100%;max-width:none;border-radius:0;box-shadow:none;background:transparent;
  gap:clamp(20px,3vw,44px);
  padding:22px var(--jk-gut);
  border-bottom:1px solid transparent;
  transition:padding .45s var(--ease-out),background .45s var(--ease-out),
             border-color .45s var(--ease-out),backdrop-filter .45s var(--ease-out);
}
.pillnav::before{display:none;}          /* the frosted lozenge */
.menu__pill{display:none;}               /* the sliding selector — replaced by rules */

/* the bar earns a surface only once the page has moved under it */
.navshell.is-stuck .pillnav{
  padding-block:13px;
  background:rgba(250,247,242,.88);
  backdrop-filter:blur(16px) saturate(1.25);
  -webkit-backdrop-filter:blur(16px) saturate(1.25);
  border-bottom-color:var(--line);
}

/* brand — the scaffold hides the wordmark in the pill because there was no
   room for it. There is room now, and the lockup is the most elegant thing
   this business owns. */
.pillnav .brand{color:var(--ink);gap:11px;}
.pillnav .brand__name{display:block;}
.brand__logo{
  background:none;box-shadow:none;border-radius:0;
  width:40px;height:40px;
  transition:width .45s var(--ease-out),height .45s var(--ease-out),transform .5s var(--ease-out);
}
.navshell.is-stuck .brand__logo{width:32px;height:32px;}
.brand:hover .brand__logo{transform:rotate(-3deg) scale(1.04);}
.pillnav .brand__name{
  font-family:var(--font-accent);font-weight:400;font-size:.92rem;
  letter-spacing:.2em;text-transform:uppercase;line-height:1.15;color:var(--ink);
}
.pillnav .brand__name span{
  font-family:var(--font-body);font-size:.5rem;letter-spacing:.32em;font-weight:500;
  color:var(--cyan-deep);margin-top:4px;
}

/* links */
.pillnav .menu{gap:clamp(4px,1.1vw,14px);}
.pillnav .menu>li>a,.pillnav .menu>li>button{
  position:relative;border-radius:0;background:none;
  font-family:var(--font-body);font-weight:500;font-size:.68rem;
  letter-spacing:.17em;text-transform:uppercase;
  color:var(--ink-soft);padding:.5em .55em .55em;
  transition:color .35s var(--ease-out);
}
.pillnav .menu>li>a::after,.pillnav .menu>li>button::after{
  content:"";position:absolute;left:.55em;right:.55em;bottom:0;height:1px;
  background:var(--cyan);transform:scaleX(0);transform-origin:right;
  transition:transform .5s var(--ease-out);
}
.pillnav .menu>li>a:hover,.pillnav .menu>li>button:hover{color:var(--ink);}
.pillnav .menu>li>a:hover::after,.pillnav .menu>li>button:hover::after{
  transform:scaleX(1);transform-origin:left;
}
.pillnav .menu>li>a[aria-current="page"]{color:var(--ink);}
.pillnav .menu>li>a[aria-current="page"]::after{transform:scaleX(1);background:var(--ink);}

/* dropdowns inherit the ivory surface rather than the navy one */
.pillnav .dropdown{
  background:rgba(250,247,242,.94);border:1px solid var(--line);
  box-shadow:0 24px 60px -24px rgba(34,32,29,.3);
}
.pillnav .dropdown a{color:var(--ink-soft);font-family:var(--font-body);font-size:.82rem;}
.pillnav .dropdown a:hover{background:var(--cream-2);color:var(--ink);}

/* CTA — outlined, fills on hover.

   Driven through the scaffold's own --bg/--fg custom properties rather than a
   ::before sweep. The first version copied a fill-from-below pattern from a
   standalone mockup, but this scaffold's .btn has no ::before with content and
   wraps no <span> around its label — so the fill never arrived while the text
   still turned ivory, and the button read as blank against the ivory page. */
.pillnav .nav__actions .btn--accent{
  --bg:transparent; --fg:var(--ink);
  border:1px solid var(--ink);
  font-family:var(--font-body);font-size:.66rem;font-weight:500;
  letter-spacing:.17em;text-transform:uppercase;
  padding:.92em 1.5em;box-shadow:none;
  transition:background-color .4s var(--ease-out),color .4s var(--ease-out),
             transform .4s var(--ease-out);
}
.pillnav .nav__actions .btn--accent:hover,
.pillnav .nav__actions .btn--accent:focus-visible{
  --bg:var(--ink); --fg:var(--paper);
  transform:none;box-shadow:none;
}

/* ---------------------------------------------------------------- mobile */
@media(max-width:1040px){
  .mobtop{
    top:0;left:0;right:0;padding:12px var(--jk-gut);
    background:transparent;backdrop-filter:none;-webkit-backdrop-filter:none;
    border:0;border-bottom:1px solid transparent;border-radius:0;box-shadow:none;
    justify-content:space-between;gap:14px;
    transition:padding .4s var(--ease-out),background .4s var(--ease-out),
               border-color .4s var(--ease-out),backdrop-filter .4s var(--ease-out);
  }
  .mobtop.is-stuck{
    padding-block:9px;
    background:rgba(250,247,242,.9);
    backdrop-filter:blur(16px) saturate(1.25);
    -webkit-backdrop-filter:blur(16px) saturate(1.25);
    border-bottom-color:var(--line);
  }
  .mobtop .brand{color:var(--ink);max-width:none;gap:10px;}
  .mobtop .brand__logo{width:34px;height:34px;}
  /* The scaffold collapses the logo into the burger on scroll-down. On a
     flush bar that reads as the logo falling off, so it stays put. */
  .mobtop.is-min .brand{max-width:none;opacity:1;transform:none;margin:0;}
  .mobtop .brand__name{
    display:block;font-family:var(--font-accent);font-weight:400;font-size:.8rem;
    letter-spacing:.18em;text-transform:uppercase;color:var(--ink);
  }
  .mobtop .brand__name span{
    font-family:var(--font-body);font-size:.44rem;letter-spacing:.3em;
    color:var(--cyan-deep);margin-top:3px;
  }

  /* burger — two hairlines that cross */
  .mobtop__burger{
    background:none;border:0;box-shadow:none;width:auto;height:auto;
    display:inline-flex;align-items:center;gap:10px;padding:8px 0;flex:none;
  }
  .mobtop__burger::before{
    content:"Menu";font-family:var(--font-body);font-size:.6rem;font-weight:500;
    letter-spacing:.2em;text-transform:uppercase;color:var(--ink-soft);
    transition:color .3s;
  }
  body.menu-open .mobtop__burger::before{content:"Close";}
  .burger{width:24px;height:9px;}
  .burger span{
    position:absolute;left:0;right:0;height:1px;background:var(--ink);border-radius:0;
    transition:transform .45s var(--ease-out),opacity .3s;
  }
  .burger span:nth-child(1){top:0;}
  .burger span:nth-child(2){top:8px;}
  body.menu-open .burger span:nth-child(1){transform:translateY(4px) rotate(45deg);}
  body.menu-open .burger span:nth-child(2){transform:translateY(-4px) rotate(-45deg);}

  /* ---- the overlay ---- */
  .mobmenu{
    background:var(--paper);
    padding:calc(58px + env(safe-area-inset-top)) var(--jk-gut) 40px;
    transform:none;
    transition:opacity .4s var(--ease-out),visibility .4s;
  }
  .mobmenu__head{display:none;}          /* .mobtop already shows the brand above it */
  .mobmenu .menu{margin-top:8px;}
  .mobmenu .menu>li{border-bottom:1px solid var(--line);background:none;}
  .mobmenu .menu>li:first-child{border-top:1px solid var(--line);}
  .mobmenu .menu>li>a,.mobmenu .menu>li>button{
    font-family:var(--font-accent);font-weight:300;
    font-size:clamp(1.7rem,8.5vw,2.3rem);letter-spacing:-.01em;text-transform:none;
    color:var(--ink);padding:.55em .1em;border-radius:0;
  }
  .mobmenu .menu>li>a::after{display:none;}
  .mobmenu .menu>li>a[aria-current="page"]{color:var(--cyan-deep);font-style:italic;}

  /* stagger — the scaffold already fades each row in; these are the delays */
  .mobmenu .menu>li{transform:translateY(16px);}
  body.menu-open .mobmenu .menu>li:nth-child(1){transition-delay:.06s;}
  body.menu-open .mobmenu .menu>li:nth-child(2){transition-delay:.11s;}
  body.menu-open .mobmenu .menu>li:nth-child(3){transition-delay:.16s;}
  body.menu-open .mobmenu .menu>li:nth-child(4){transition-delay:.21s;}
  body.menu-open .mobmenu .menu>li:nth-child(5){transition-delay:.26s;}
  body.menu-open .mobmenu .menu>li:nth-child(6){transition-delay:.31s;}
  body.menu-open .mobmenu .menu>li:nth-child(7){transition-delay:.36s;}
  body.menu-open .mobmenu__actions{transition-delay:.42s;}

  .mobmenu__actions{
    margin-top:auto;padding-top:30px;display:flex;flex-direction:column;gap:12px;
  }
  .mobmenu__actions .btn{width:100%;justify-content:center;padding:1.05em;
    font-family:var(--font-body);font-size:.7rem;letter-spacing:.17em;text-transform:uppercase;}
  .mobmenu__actions .btn--ghost{border:1px solid var(--line);color:var(--ink-soft);}

  /* a closing line, so the panel ends on the brand rather than a button edge */
  .mobmenu__foot{
    margin-top:26px;padding-top:20px;border-top:1px solid var(--line);
    display:flex;flex-wrap:wrap;gap:6px 18px;align-items:baseline;
  }
  .mobmenu__foot a,.mobmenu__foot span{
    font-family:var(--font-body);font-size:.72rem;color:var(--ink-soft);font-weight:300;
  }
  .mobmenu__foot a:hover{color:var(--cyan-deep);}
  .mobmenu__foot em{
    font-family:var(--font-head);font-style:italic;font-size:.82rem;color:var(--cyan-deep);
    flex-basis:100%;
  }
}

@media(prefers-reduced-motion:reduce){
  .pillnav,.mobtop,.brand__logo,.mobmenu .menu>li{transition-duration:.001s!important;}
  .mobmenu .menu>li{transform:none;}
}

/* The nav mark is inked for the ivory masthead. The client portal has a dark
   theme, and there the same file would be ink on near-black. */
html.dark .pillnav .brand__logo img,
html.dark .mobtop .brand__logo img,
html.dark .mobmenu .brand__logo img{filter:invert(1) brightness(1.6);}

/* The brand is type, so the bar's children need to spread: brand at the left
   gutter, links and CTA pushed to the right one. Without this the whole bar
   packed left and left 300px of dead space at 1280. */
.pillnav .brand{margin-right:auto;}
.pillnav .brand__name,
.mobtop .brand__name{display:block;}
.mobmenu .brand__logo,.pillnav .brand__logo,.mobtop .brand__logo{display:none;}

/* The scaffold frosts the mobile panel: a translucent blue-gray glass with a
   30px backdrop blur, so the page shows through it. Over a hero photograph
   that leaves 2.3rem serif links sitting on a bride's veil. The selector below
   matches the scaffold's own specificity (html:not(.app-mode) .mobmenu) so it
   can win, and the panel becomes a solid ground. The installed app keeps its
   glass sheet — there it slides up over the app's own flat surfaces. */
html:not(.app-mode) .mobmenu{
  background:var(--paper);
  backdrop-filter:none;-webkit-backdrop-filter:none;
}
/* and the rows are rules on that ground, not floating white pills */
html:not(.app-mode) .mobmenu .menu>li{
  background:none;border-radius:0;overflow:visible;
  border-bottom:1px solid var(--line);
}
html:not(.app-mode) .mobmenu .menu>li:first-child{border-top:1px solid var(--line);}
html:not(.app-mode) .mobmenu .menu>li>a,
html:not(.app-mode) .mobmenu .menu>li>button{
  padding:.55em .1em;font-size:clamp(1.7rem,8.5vw,2.3rem);
}

/* Six rows at 2.3rem filled the whole screen and pushed the buttons and the
   contact line off the bottom of a 844px phone. The type is still the largest
   thing on the panel; it just stops being the only thing on it. */
@media(max-width:1040px){
  html:not(.app-mode) .mobmenu .menu>li>a,
  html:not(.app-mode) .mobmenu .menu>li>button{
    font-size:clamp(1.35rem,6.4vw,1.75rem);padding:.46em .1em;
  }
  .mobmenu{padding-bottom:26px;}
  .mobmenu__actions{padding-top:22px;gap:10px;}
  .mobmenu__actions .btn{padding:.92em;}
  .mobmenu__foot{margin-top:18px;padding-top:16px;}
}

/* Short handsets — a 640px-tall Android still had to scroll to reach the
   buttons. A nav should never need scrolling to see all of itself. */
@media(max-width:1040px) and (max-height:720px){
  html:not(.app-mode) .mobmenu .menu>li>a,
  html:not(.app-mode) .mobmenu .menu>li>button{
    font-size:clamp(1.15rem,5.4vw,1.4rem);padding:.36em .1em;
  }
  .mobmenu{padding-top:calc(52px + env(safe-area-inset-top));padding-bottom:18px;}
  .mobmenu__actions{padding-top:16px;gap:8px;}
  .mobmenu__actions .btn{padding:.8em;font-size:.64rem;}
  .mobmenu__foot{margin-top:12px;padding-top:12px;}
  .mobmenu__foot em{font-size:.74rem;}
  .mobmenu__foot a,.mobmenu__foot span{font-size:.66rem;}
}

/* =========================================================
   INQUIRY FORM — matched to the site
   ---------------------------------------------------------
   render_form() gives us submissions in the admin, editable fields and email
   delivery, all of which are worth keeping. What it does not give us is this
   site's look: it renders a dark glass card floating on a blurred photograph,
   which is the scaffold's church-hero language and reads like a different
   website bolted onto the contact page.

   So the machinery stays and the surface changes: ivory ground, hairline
   fields, a left-aligned serif heading, taupe focus rings. Scoped to the
   public site so the admin's own preview is untouched.
   ========================================================= */
.formstage{
  padding:0;display:block;overflow:visible;background:none;isolation:auto;
}
.formstage__bg{display:none;}          /* the blurred photo behind the card */

.form{
  max-width:none;color:var(--ink);
  background:var(--cream);
  backdrop-filter:none;-webkit-backdrop-filter:none;
  border:1px solid var(--line);
  border-radius:var(--radius-card);
  padding:clamp(26px,3.4vw,44px);
  gap:18px;box-shadow:none;
}
.form__title{
  text-align:left;color:var(--ink);
  font-family:var(--font-accent);font-weight:300;
  font-size:clamp(1.6rem,1.2rem + 1.2vw,2.2rem);line-height:1.1;
}
.form__sub{
  text-align:left;margin:0;max-width:52ch;
  color:var(--ink-soft);font-weight:300;font-size:.95rem;line-height:1.7;
}

/* labels — the scaffold styles standalone fields with a bare `.field label`
   at rgba(244,238,225,.92), which is near-white and invisible on cream. That
   selector has to be answered as well as the .lbl one. */
.form .field label{
  font-family:var(--font-body);font-weight:500;
  font-size:.6rem;letter-spacing:.18em;text-transform:uppercase;
  color:var(--cyan-deep);
}
.form .lbl,.namefield>.lbl{
  font-family:var(--font-body);font-weight:500;
  font-size:.6rem;letter-spacing:.18em;text-transform:uppercase;
  color:var(--cyan-deep);margin-bottom:2px;
}
.form .field label.sub,.namefield .field label.sub{
  font-family:var(--font-body);font-weight:500;
  font-size:.58rem;letter-spacing:.16em;text-transform:uppercase;color:var(--cyan-deep);
}
.form .opt,.namefield .opt{color:var(--muted);letter-spacing:.08em;text-transform:none;font-size:.62rem;}
.form .field .req{color:var(--cyan);}

/* inputs */
.form .field input,.form .field select,.form .field textarea{
  font-family:var(--font-body);font-weight:300;font-size:.96rem;
  color:var(--ink);background:var(--paper);
  border:1px solid var(--line);border-radius:12px;
  padding:.85em .95em;
}
.form .field textarea{min-height:132px;resize:vertical;}
.form .field input::placeholder,.form .field textarea::placeholder{color:var(--muted);opacity:.8;}
.form .field input:focus,.form .field select:focus,.form .field textarea:focus{
  border-color:var(--cyan);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--cyan) 22%,transparent);
  background:var(--paper);
}
.form .field select{
  appearance:none;-webkit-appearance:none;cursor:pointer;
  background-image:linear-gradient(45deg,transparent 50%,var(--cyan-deep) 50%),
                   linear-gradient(135deg,var(--cyan-deep) 50%,transparent 50%);
  background-position:calc(100% - 19px) calc(50% + 1px),calc(100% - 14px) calc(50% + 1px);
  background-size:5px 5px,5px 5px;background-repeat:no-repeat;
  padding-right:2.4em;
}

/* the "what are you interested in" pills */
.form .checks label{
  font-family:var(--font-body);font-weight:400;font-size:.84rem;
  letter-spacing:0;text-transform:none;
  border:1px solid var(--line);background:var(--paper);color:var(--ink-soft);
  padding:.62em 1.05em;
}
.form .checks label:hover{border-color:var(--cyan);color:var(--ink);}
.form .checks label:has(input:checked){
  background:var(--ink);border-color:var(--ink);color:var(--paper);
}
.form .checks label:has(input:focus-visible){outline:2px solid var(--cyan);outline-offset:2px;}
.form .checkgroup__err{color:#a6462f;font-size:.82rem;}

/* The checkbox group's <legend> carries its own near-white colour, separate
   from both .lbl and `.field label`. Third selector, same problem. */
.form .checkgroup>legend{
  font-family:var(--font-body);font-weight:500;
  font-size:.6rem;letter-spacing:.18em;text-transform:uppercase;
  color:var(--cyan-deep);margin-bottom:10px;
}

/* Submit. The scaffold uses .btn--light here — an ivory button, which is right
   on its dark glass card and invisible on our cream one. */
.form .btn{justify-content:center;padding:1.05em;font-family:var(--font-body);
  font-size:.7rem;letter-spacing:.18em;text-transform:uppercase;}
.form .btn--light{
  --bg:var(--ink); --fg:var(--paper);
  border:1px solid var(--ink);box-shadow:none;
  transition:background-color .4s var(--ease-out),color .4s var(--ease-out),
             transform .4s var(--ease-out);
}
.form .btn--light:hover,.form .btn--light:focus-visible{
  --bg:var(--cyan-deep); --fg:var(--paper);
  border-color:var(--cyan-deep);transform:translateY(-2px);
}
.form .note,.form small{color:var(--muted);font-size:.78rem;font-weight:300;}

/* the success state after a submission */
.form__done,.formdone{color:var(--ink);}

@media(max-width:900px){
  .form{padding:clamp(22px,5vw,30px);border-radius:var(--radius);}
}

/* =========================================================
   MASTHEAD OVER A DARK BANNER
   ---------------------------------------------------------
   Every public page on this site opens on ivory, or on a hero photograph
   whose top band is bright, so the masthead is inked and that is right.

   The client area is the exception: its pages open on a full-bleed photograph
   with a dark scrim over it, and there the same espresso links sat on a dark
   photo and all but disappeared — "HOME PHOTOGRAPHY FILMS" as a grey smudge
   across a bride's veil.

   Rather than tag those templates by hand (and have the next one arrive
   wrong), this asks the document: does <main> open on a photo banner? If so
   the bar goes ivory — but only until it sticks, because the moment the page
   scrolls the bar gains its own ivory surface and the ink is right again.
   ========================================================= */
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .brand,
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .brand__name{
  color:var(--paper);
  text-shadow:0 1px 14px rgba(0,0,0,.45);
}
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .brand__name span{
  color:rgba(250,247,242,.72);
}
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .menu>li>a,
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .menu>li>button{
  color:rgba(250,247,242,.82);
  text-shadow:0 1px 14px rgba(0,0,0,.45);
}
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .menu>li>a:hover,
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .menu>li>button:hover,
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .menu>li>a[aria-current="page"]{
  color:var(--paper);
}
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .menu>li>a[aria-current="page"]::after{
  background:var(--paper);
}
/* The two buttons at the right of the bar: outlined ivory rather than ink. */
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .btn--ghost,
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .btn--accent{
  --bg:transparent; --fg:var(--paper);
  border-color:rgba(250,247,242,.5);
}
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .btn--ghost:hover,
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .btn--accent:hover{
  --bg:var(--paper); --fg:var(--ink);
  border-color:var(--paper);
}
/* Mobile: the same bar, same test. The burger's two bars are drawn with
   `background:currentColor`, so setting the button's color is enough — they
   follow it without a rule of their own. */
body:has(main > .banner--photo:first-child) .mobtop:not(.is-stuck) .brand,
body:has(main > .banner--photo:first-child) .mobtop:not(.is-stuck) .brand__name,
body:has(main > .banner--photo:first-child) .mobtop:not(.is-stuck) .mobtop__burger{
  color:var(--paper);
  text-shadow:0 1px 14px rgba(0,0,0,.45);
}
/* The word and the two bars name their own colors (--ink-soft and --ink), so
   setting the button's `color` does not reach them. */
body:has(main > .banner--photo:first-child) .mobtop:not(.is-stuck) .mobtop__burger::before{
  color:rgba(250,247,242,.82);text-shadow:0 1px 14px rgba(0,0,0,.45);
}
body:has(main > .banner--photo:first-child) .mobtop:not(.is-stuck) .burger span{
  background:var(--paper);box-shadow:0 1px 10px rgba(0,0,0,.4);
}
body:has(main > .banner--photo:first-child) .mobtop:not(.is-stuck) .brand__name span{
  color:rgba(250,247,242,.7);
}
/* With the panel open the bar sits on ivory in every case, so it goes back
   to ink — otherwise "Close" and the cross would be white on white. */
body.menu-open:has(main > .banner--photo:first-child) .mobtop .brand,
body.menu-open:has(main > .banner--photo:first-child) .mobtop .brand__name,
body.menu-open:has(main > .banner--photo:first-child) .mobtop .mobtop__burger{
  color:var(--ink);text-shadow:none;
}
body.menu-open:has(main > .banner--photo:first-child) .mobtop .mobtop__burger::before{
  color:var(--ink-soft);text-shadow:none;
}
body.menu-open:has(main > .banner--photo:first-child) .mobtop .burger span{
  background:var(--ink);box-shadow:none;
}
body.menu-open:has(main > .banner--photo:first-child) .mobtop .brand__name span{
  color:var(--cyan-deep);
}

/* =========================================================
   SCROLL-LINKED MOTION — the CSS half
   ---------------------------------------------------------
   motion.js drives the transforms. What lives here is the
   two things CSS is better at: the resting state of anything
   the script will move, and the small rules that draw
   themselves once a section has arrived.

   Everything is gated on html.motion. The <head> adds that
   class and removes it again if the motion module has not
   loaded within two seconds, so on a failed import — or a
   browser that never runs the module at all — none of these
   hidden states apply and the page is simply static.
   ========================================================= */

/* ---- rules that draw themselves ----
   motion.js marks an element data-shown the moment it reveals, so these ride
   the reveal without needing a second observer. The kicker's dash grows from
   its left edge as the words beside it arrive. */
html.motion [data-reveal] .jk-kicker i,
html.motion [data-reveal].jk-kicker i{
  transform:scaleX(0);transform-origin:0 50%;
}
html.motion [data-reveal][data-shown] .jk-kicker i,
html.motion [data-reveal][data-shown].jk-kicker i{
  transform:scaleX(1);
  transition:transform .9s var(--ease-out) .18s;
}
/* The signature rule under the philosophy pull-quote, same idea. */
html.motion [data-reveal] .jkphil__sign i{transform:scaleX(0);transform-origin:0 50%;}
html.motion [data-reveal][data-shown] .jkphil__sign i{
  transform:scaleX(1);transition:transform .9s var(--ease-out) .3s;
}

/* ---- the marquee's scroll-driven wrapper ----
   A plain wrapper with no styling of its own: it exists so the scrub transform
   and the CSS keyframe animation have an element each, rather than fighting
   over one. */
.jkband__scrub{display:block;will-change:transform;
  /* The scroll nudge moves this up to 90px either way; starting 100px to the
     left means the band's left edge never comes into view. */
  margin-left:-100px;}

/* ---- drifting and settling photographs ----
   The script writes the transform. This is only the promise that the container
   clips it and that the browser plans for the movement. */
[data-drift],[data-settle]{overflow:hidden;}
html.motion [data-drift] img,
html.motion [data-settle] img{will-change:transform;}

/* ---- per-tile gallery reveals ----
   These wait for their own arrival rather than a group trigger, so they start
   hidden the same way [data-reveal] does. Same html.motion gate, so a page
   with no script shows every photograph. */
html.motion [data-reveal-each] > *{opacity:0;}
/* Direct children only, and only of the grid itself. The script used to stamp
   data-reveal-each onto each tile as well, which made the selector above match
   the tiles' OWN children too — every figure inside a gallery button, every
   heading inside a package card — and nothing was ever going to animate those
   back to visible. */

@media(prefers-reduced-motion:reduce){
  html.motion [data-reveal] .jk-kicker i,
  html.motion [data-reveal].jk-kicker i,
  html.motion [data-reveal] .jkphil__sign i{transform:none;}
  html.motion [data-reveal-each] > *{opacity:1;}
  .jkband__scrub{transform:none!important;}
}

/* ---- the masthead steps out of the way of a photograph ----
   The viewers are full-screen and 95% opaque, so the ivory bar underneath used
   to ghost through as a lighter band with the nav links faintly legible on it.
   It also had nothing to offer while a photograph is open. Gone for the
   duration, and the bar cannot re-show itself mid-viewer because the scroll
   handler that toggles .is-stuck has nothing to react to with the body
   locked. */
body.jk-viewer-open .navshell,
body.jk-viewer-open .mobtop,
body.jk-viewer-open .botbar{opacity:0;pointer-events:none;transition:opacity .28s var(--ease-out);}

/* =========================================================
   FOOTER
   ---------------------------------------------------------
   The scaffold's footer is a church's: a logo lockup, three columns of link
   lists under heavy underlined headings, four solid-white social discs, and an
   appearance switcher sitting dead centre of the bottom bar.

   Every one of those is louder than the thing above it. On a page that ends
   with a photograph, the footer should be the quietest surface on the site —
   a place to find a link, not a second navigation competing with the first.
   ========================================================= */

/* ---- brand: the masthead's wordmark, not a lockup ---- */
.footer__brand .brand{gap:0;}
.footer__brand .brand__name{
  font-family:var(--font-accent);font-weight:400;font-size:1.02rem;
  letter-spacing:.2em;text-transform:uppercase;line-height:1.2;
  color:var(--ink-invert);
}
.footer__brand .brand__name span{
  display:block;font-family:var(--font-body);font-size:.52rem;letter-spacing:.32em;
  font-weight:500;margin-top:6px;color:rgba(244,238,225,.5);
}
.footer__brand p{max-width:34ch;}

/* ---- column headings: a kicker, not a banner ----
   Underlined bold caps in the display face read as headings of equal weight to
   the page's own. These are labels above a short list, and should look like
   the kickers everywhere else on the site. */
.footer h4{
  font-family:var(--font-body);font-weight:500;font-size:.62rem;
  letter-spacing:.26em;text-transform:uppercase;
  /* .45 was the look, and it measured 3.95:1 against the footer's ground —
     below AA for text this small. .55 is 5.2:1 and reads the same. */
  color:rgba(244,238,225,.55);
  text-decoration:none;
  margin-bottom:18px;
}
.footer ul{display:flex;flex-direction:column;gap:2px;}
.footer ul a{
  font-size:.94rem;font-weight:300;color:rgba(244,238,225,.78);
  padding-block:5px;display:inline-block;
  transition:color .35s var(--ease-out),transform .35s var(--ease-out);
}
.footer ul a:hover{color:var(--ink-invert);transform:translateX(3px);}

/* ---- socials: outlined, not four white discs ----
   The PNGs are solid white glyphs on a white disc, which made them the
   brightest objects on the darkest surface of the site — brighter than the
   business's own name two lines above. Knocked back to the footer's own
   register, and they come up to full strength on hover. */
.footer .socials{gap:10px;margin-top:26px;}
.footer .socials a{
  width:38px;height:38px;border:1px solid rgba(244,238,225,.22);
  align-items:center;justify-content:center;
  transition:border-color .35s var(--ease-out),transform .35s var(--ease-out);
}
.footer .socials a img{
  width:19px;height:19px;opacity:.62;
  transition:opacity .35s var(--ease-out);
}
.footer .socials a:hover{
  border-color:rgba(244,238,225,.55);transform:translateY(-2px) scale(1);filter:none;
}
.footer .socials a:hover img{opacity:1;}

/* ---- bottom bar: copyright, name, and a switch that knows its place ----
   The appearance control was a filled pill in the centre of the bar, which
   made a per-visitor preference the most prominent thing in the footer. It
   keeps working; it stops shouting. */
.footer__bottom{align-items:center;font-size:.8rem;}
.footer__acts{margin-left:auto;order:3;}
.footer__svc{order:2;font-family:var(--font-head);font-style:italic;font-size:.95rem;
  color:rgba(244,238,225,.55);}
.footer .fsch{
  padding:2px;border-color:rgba(244,238,225,.16);background:transparent;
  transform:scale(.86);transform-origin:right center;
}
/* The sliding thumb was solid ivory, and the selected label was espresso to
   read on it. Knocking the thumb back to a tint leaves that espresso label on
   a near-black ground — invisible. Selected text goes ivory to match. */
.footer .fsch::before{background:rgba(244,238,225,.16);}
.footer .fsch:has(.fsch__o:hover input:checked)::before{background:rgba(244,238,225,.24);}
.footer .fsch__o span{font-size:.62rem;letter-spacing:.12em;text-transform:uppercase;
  font-weight:500;min-height:32px;color:rgba(244,238,225,.6);}
.footer .fsch__o input:checked+span{color:var(--ink-invert);}
.footer .fsch__o:hover input:not(:checked)+span{color:var(--ink-invert);background:transparent;}

@media(max-width:900px){
  .footer__acts{margin-left:0;}
  .footer .fsch{transform-origin:left center;}
}

/* ---- rail arrows (assets/js/jk.js) ----
   The two home rails scroll sideways with no scrollbar, which a mouse cannot
   do. Arrows sit over each end; on a touch screen, where a swipe already
   works, they stay out of the way. */
.jkrail{position:relative;}
.jkquotes__rail{cursor:grab;}
.jkquotes__rail.is-drag{cursor:grabbing;scroll-snap-type:none;}
.jkstrip__rail.is-drag a,.jkquotes__rail.is-drag *{pointer-events:none;user-select:none;}
.jkstrip__rail:focus-visible,.jkquotes__rail:focus-visible{outline:2px solid var(--cyan);outline-offset:4px;}
.jkrail__nav{position:absolute;top:50%;z-index:3;width:48px;height:48px;margin-top:-24px;border-radius:50%;
  display:grid;place-items:center;cursor:pointer;color:var(--ink);background:var(--paper);
  border:1px solid var(--line);box-shadow:0 10px 26px -12px rgba(34,32,29,.35);
  transition:opacity .3s var(--ease-out),background .3s var(--ease-out),color .3s var(--ease-out),transform .3s var(--ease-out);}
.jkrail__nav--prev{left:max(4px,calc(var(--jk-gut) - 24px));}
.jkrail__nav--next{right:max(4px,calc(var(--jk-gut) - 24px));}
.jkrail__nav:hover{background:var(--ink);color:var(--paper);transform:scale(1.05);}
.jkrail__nav:focus-visible{outline:2px solid var(--cyan);outline-offset:3px;}
.jkrail__nav:disabled{opacity:0;pointer-events:none;}
.jkrail--static .jkrail__nav{display:none;}
@media (hover:none){.jkrail__nav{display:none;}}
@media (prefers-reduced-motion:reduce){.jkrail__nav{transition:none;}}

/* The last card snaps by its end, so the rail can reach it whole instead of
   stopping with it cut off at the edge of the window. */
.jkstrip__rail > a:last-child,.jkquote:last-child{scroll-snap-align:end;}

/* ---- no page scrollbar ----
   The page still scrolls exactly as before (wheel, trackpad, touch, keys);
   only the grey bar down the right-hand side is gone, so the ivory runs edge
   to edge. Public pages only: the admin loads its own stylesheet and keeps
   its bars, where long lists need them. */
html{scrollbar-width:none;-ms-overflow-style:none;}
html::-webkit-scrollbar,body::-webkit-scrollbar{display:none;width:0;height:0;}

/* =========================================================
   The signed-in client's name in the bar.
   The scaffold draws it for a dark pill — ivory text on a smoky wash — and
   on this ivory bar that came out as a faint circle with no name beside it.
   Here it reads like the rest of the bar: ink, a hairline, the menu's caps.
   ========================================================= */
.pillnav .navmem__btn{
  color:var(--ink);background:transparent;
  border:1px solid color-mix(in srgb,var(--ink) 22%,transparent);
  font-family:var(--font-body);font-size:.66rem;font-weight:500;
  letter-spacing:.17em;text-transform:uppercase;
  padding:.5em 1.1em .5em .5em;
}
.pillnav .navmem__btn:hover,
.pillnav .navmem:focus-within .navmem__btn{background:var(--cream-2);border-color:color-mix(in srgb,var(--ink) 40%,transparent);}
.pillnav .navmem__ava{background:var(--ink);color:var(--paper);}
.pillnav .navmem__ava svg{width:14px;height:14px;margin:5px;}
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .navmem__btn{
  color:var(--paper);border-color:rgba(250,247,242,.5);text-shadow:0 1px 14px rgba(0,0,0,.45);
}
body:has(main > .banner--photo:first-child) .navshell:not(.is-stuck) .pillnav .navmem__ava{background:var(--paper);color:var(--ink);}

/* Page editor: the photo buttons sit along the bottom edge. Most photos here
   are arched across the top, and a button pinned top-left was cut in half by
   the curve ("…ground", "…on / zoom"). No photo on this site curves at the
   bottom-left, so every button is whole there. */
body.edit-mode .eimg-btn{top:auto;bottom:52px;left:14px;}
body.edit-mode .eimg-adjust{top:auto;bottom:14px;left:14px;}
/* A full-bleed photo's buttons live on its section, whose foot is under the
   editing bar when the page first opens; lift them clear of it. */
body.edit-mode section > .eimg-btn{bottom:112px;}
body.edit-mode section > .eimg-adjust{bottom:74px;}

/* Portfolio, laid out by script into columns that read left to right (see
   photography/index.php). Without script it stays the CSS-columns masonry. */
.jkgal.is-cols{columns:auto;display:flex;align-items:flex-start;gap:clamp(10px,1.4vw,20px);}
.jkgal.is-cols > .jkgal__col{flex:1 1 0;min-width:0;}
.jkgal__col > button{break-inside:avoid;margin-bottom:clamp(10px,1.4vw,20px);display:block;
  width:100%;padding:0;border:0;background:none;cursor:zoom-in;}
.jkgal__col > button[hidden]{display:none;}
.jkgal__col > button:hover .jk-fig img{transform:scale(calc(var(--zx) * 1.04));}
.jkgal__col > button:focus-visible{outline:2px solid var(--cyan);outline-offset:4px;}

/* Phones: two across unless the editor chose one (masonry without script). */
@media (max-width:520px){ .jkgal--ph2{columns:2;} }

/* The other portfolio layouts (chosen in the page editor, gal_layout). Each
   photograph's shape rides on its button as --ar (width / height). */
.jkgal--rows,.jkgal--grid,.jkgal--mosaic{columns:auto;--g:clamp(8px,1.2vw,16px);gap:var(--g);}
.jkgal--rows > button,.jkgal--grid > button,.jkgal--mosaic > button{margin:0;}

/* Rows: even-height strips. Each frame grows in proportion to its own shape,
   so a row lines up at both ends with no cropping; the last row keeps its
   natural size rather than stretching to fill. */
.jkgal--rows{display:flex;flex-wrap:wrap;--rh:300px;}
.jkgal--rows > button{width:auto;min-width:0;flex:var(--ar,1.5) 1 calc(var(--ar,1.5) * var(--rh));}
.jkgal--rows::after{content:"";flex:999 1 0;}
.jkgal--rows .jk-fig img{aspect-ratio:var(--ar,1.5);}
@media (max-width:900px){ .jkgal--rows{--rh:210px;} }
@media (max-width:520px){ .jkgal--rows.jkgal--ph2{--rh:160px;} .jkgal--rows.jkgal--ph1{--rh:230px;} }

/* Grid: even squares. */
.jkgal--grid{display:grid;grid-template-columns:repeat(3,1fr);}
.jkgal--grid .jk-fig img{aspect-ratio:1;height:100%;}
@media (max-width:520px){ .jkgal--grid.jkgal--ph2{grid-template-columns:repeat(2,1fr);} .jkgal--grid.jkgal--ph1{grid-template-columns:1fr;} }

/* Mosaic: large, tall, wide and small frames. The pattern and the row height
   are set by script from the width available; this is the fallback. */
.jkgal--mosaic{display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:clamp(150px,18vw,240px);}
.jkgal--mosaic > button,.jkgal--mosaic .jk-fig{height:100%;}
.jkgal--mosaic .jk-fig img{height:100%;}
@media (max-width:900px){ .jkgal--mosaic{grid-template-columns:repeat(2,1fr);grid-auto-rows:44vw;} }

/* "Is that date open?" under a date field (assets/js/datecheck.js). */
.datecheck{grid-column:1/-1;margin:6px 0 0;font-size:.82rem;line-height:1.45;display:flex;gap:7px;align-items:flex-start}
.datecheck::before{content:"";flex:none;width:8px;height:8px;border-radius:50%;margin-top:.45em;background:currentColor}
.datecheck.is-open{color:#2f6b4f}
.datecheck.is-booked{color:#8a4a12}
.datecheck.is-past{color:var(--ink-soft)}

/* Blocks placed at the top of a hand-built page (Site editor → Where on the
   page: Top) sit under the fixed menu, so they start clear of it; the page's
   own heading below them then needs far less room above it. */
.cmsextras--top{padding-top:clamp(64px,7vw,88px);}
.cmsextras--top + .jkhead{padding-top:clamp(36px,5vw,72px);}

/* iOS draws a date field at its own width and centres the date in it, so it
   stood out past every other box on the contact form. */
.field input[type=date]{display:block;width:100%;min-width:0;max-width:100%;box-sizing:border-box;text-align:left;}
@supports (-webkit-touch-callout:none){
  .field input[type=date]{-webkit-appearance:none;appearance:none;height:calc(1.2em + 1.7em + 2px);line-height:1.2;}
  .field input[type=date]::-webkit-date-and-time-value{text-align:left;margin:0;}
}
