/* ---------- brand fonts (self-hosted, see /brand-assets) ---------- */
@font-face{
  font-family:'Montserrat'; font-style:normal; font-weight:100 900;
  font-display:swap; src:url('assets/fonts/montserrat-variable.woff2') format('woff2-variations');
}
@font-face{
  font-family:'Montserrat'; font-style:italic; font-weight:100 900;
  font-display:swap; src:url('assets/fonts/montserrat-italic-variable.woff2') format('woff2-variations');
}
@font-face{
  font-family:'Poppins'; font-style:normal; font-weight:400;
  font-display:swap; src:url('assets/fonts/poppins-regular.woff2') format('woff2');
}
@font-face{
  font-family:'Poppins'; font-style:normal; font-weight:500;
  font-display:swap; src:url('assets/fonts/poppins-medium.woff2') format('woff2');
}
@font-face{
  font-family:'Poppins'; font-style:normal; font-weight:600;
  font-display:swap; src:url('assets/fonts/poppins-semibold.woff2') format('woff2');
}
@font-face{
  font-family:'Poppins'; font-style:italic; font-weight:400;
  font-display:swap; src:url('assets/fonts/poppins-italic.woff2') format('woff2');
}

/* ---------- tokens ---------- */
:root{
  --paper:#F5F5F5;
  --paper-soft:#E5D5C5;
  --ink:#1A1A1A;
  --ink-soft:#6B6A63;
  --line:#DCD3C7;
  --accent:#D5F6B4;
  --accent-strong:#003D30;
  --accent-ink:#1A1A1A;
  --focus:var(--accent-strong);
  --dusk:#003D30;
  --dusk-text:#F5F5F5;
  --dusk-text-soft:color-mix(in srgb, var(--dusk-text) 70%, var(--accent) 30%);

  --font-display:'Poppins','Helvetica Neue',Arial,sans-serif;
  --font-body:'Montserrat','Helvetica Neue',Arial,sans-serif;
  --font-mono:'Montserrat','Helvetica Neue',Arial,sans-serif;

  --fs-hero: clamp(2.8rem, 11.5vw, 10rem);
  --fs-display: clamp(2rem, 4.2vw, 3.6rem);
  --fs-h3: clamp(1.35rem, 2vw, 1.8rem);
  --fs-body: clamp(1rem, .35vw + .92rem, 1.15rem);
  --fs-cap: .74rem;
  --fs-nav: .78rem;

  --noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.05 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");

  color-scheme: light;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    color-scheme: dark;
    --paper:#14201C;
    --paper-soft:#1C2A25;
    --ink:#F5F5F5;
    --ink-soft:#A9B5AE;
    --line:#2E3E38;
    --accent:#D5F6B4;
    --accent-strong:#D5F6B4;
  }
}
:root[data-theme="dark"]{
  color-scheme: dark;
  --paper:#14201C;
  --paper-soft:#1C2A25;
  --ink:#F5F5F5;
  --ink-soft:#A9B5AE;
  --line:#2E3E38;
  --accent:#D5F6B4;
  --accent-strong:#D5F6B4;
}
:root[data-theme="light"]{
  color-scheme: light;
}

*{box-sizing:border-box;}
html{overflow-x:hidden; overflow-x:clip;}
@media (hover:hover) and (pointer:fine){
  html{scroll-behavior:smooth;}
}
@media (prefers-reduced-motion: reduce){ html{scroll-behavior:auto;} }

body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--font-body);
  font-size:var(--fs-body);
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
  overflow-x:clip;
}
body.locked{overflow:hidden;}

h1,h2,h3{font-family:var(--font-display);font-weight:500;margin:0;text-wrap:balance;}
p{margin:0;}
a{color:inherit;}
::selection{background:var(--accent);color:var(--ink);}

:focus-visible{outline:2px solid var(--focus);outline-offset:3px;}

.eyebrow{
  font-family:var(--font-mono);
  font-size:var(--fs-cap);
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--ink-soft);
}

/* ---------- header ---------- */
.site-header{
  position:fixed; inset:0 0 auto 0; z-index:60;
  display:flex; align-items:flex-start; justify-content:space-between;
  padding:1.6rem clamp(1.2rem,3vw,2.4rem);
  pointer-events:none;
  /* Blend on the stacking-context element itself so white chrome inverts
     against whatever is behind the fixed bar (light pages → dark, dusk
     hero/footer → light). Putting mix-blend-mode on the children instead
     isolated the blend inside the header, so the logo and links stayed
     white on light backgrounds. */
  mix-blend-mode:difference;
}
.site-header *{pointer-events:auto;}
.brand-mark, .site-nav, .icon-btn, .menu-btn{
  color:#fff;
}
.brand-mark{ display:block; line-height:0; }
.brand-mark img{ display:block; height:1.3rem; width:auto; }
.site-nav{
  display:flex; align-items:center; gap:clamp(1rem,2vw,2rem);
  font-family:var(--font-mono); font-size:var(--fs-nav);
  letter-spacing:.1em; text-transform:uppercase;
}
.site-nav button, .site-nav a{
  appearance:none;background:none;border:0;color:inherit;font:inherit;
  letter-spacing:inherit;text-transform:inherit;text-decoration:none;
  cursor:pointer;padding:.2rem 0;position:relative;
}
.site-nav button::after, .site-nav a::after{
  content:'';position:absolute;left:0;right:0;bottom:-2px;height:1px;
  background:currentColor;transform:scaleX(0);transform-origin:right;
  transition:transform .3s ease;
}
.site-nav button:hover::after, .site-nav a:hover::after{transform:scaleX(1);transform-origin:left;}
.site-nav a.nav-instagram{
  display:inline-flex; align-items:center; line-height:0;
}
.site-nav a.nav-instagram::after{ content:none; }
.site-nav a.nav-instagram svg{ width:1.05em; height:1.05em; display:block; }

.menu-btn{
  display:none; flex-direction:column; justify-content:center; align-items:flex-end; gap:5px;
  width:2.2rem; height:2.2rem; background:none; border:0; cursor:pointer; padding:0;
}
.menu-btn span{ display:block; width:100%; height:1px; background:currentColor; }
.menu-btn span:nth-child(2){ width:70%; }

.mobile-nav{
  position:fixed; top:0; right:0; height:100%; width:min(340px,84vw); z-index:85;
  background:var(--paper); color:var(--ink);
  transform:translateX(100%); transition:transform .5s cubic-bezier(.2,.8,.2,1), box-shadow .5s ease;
  display:flex; flex-direction:column; padding:5.5rem 2rem 3rem;
  pointer-events:none;
}
body.menu-open .mobile-nav{
  transform:translateX(0);
  box-shadow:-30px 0 60px -20px rgba(0,0,0,.35);
  pointer-events:auto;
}
.mobile-nav .icon-close{ position:absolute; top:1.5rem; right:1.5rem; }
.mobile-nav nav{ display:flex; flex-direction:column; gap:1.7rem; }
.mobile-nav nav a, .mobile-nav nav button{
  background:none; border:0; text-align:left; padding:0; cursor:pointer;
  font-family:var(--font-display); font-size:1.6rem; color:var(--ink); text-decoration:none;
}
.mobile-nav a.nav-instagram{
  display:inline-flex; align-items:center; line-height:0;
}
.mobile-nav a.nav-instagram svg{ width:1.55rem; height:1.55rem; display:block; }

@media (max-width:720px){
  .site-nav{ display:none; }
  .menu-btn{ display:flex; }
}

/* ---------- hero ----------
   The hero is a plain full-height section, first in the .snap-viewport
   scroll flow — NOT a taller pinned/sticky spacer any more. The old
   160vh pin lived OUTSIDE .snap-viewport, in the document scroll, so the
   page had two independent scroll contexts (document + nested viewport);
   a fast flick could leave the document stuck mid-pin (hero frozen at
   the top) while the inner viewport scrolled on, which also pushed every
   feed section down so their bottoms — the overlay captions — fell below
   the fold. One scroll context, one snap track, no desync. */
.hero-pin{
  position:relative;
  height:100vh; height:100svh;
  overflow:hidden;
}
.hero-stage{
  position:relative; height:100%; overflow:hidden;
  background:var(--dusk); color:var(--dusk-text);
  display:flex; align-items:center; justify-content:center;
}
.hero-stage::after{
  content:'';position:absolute;inset:0;z-index:0;background-image:var(--noise);
  opacity:.5; mix-blend-mode:overlay; pointer-events:none;
}
.hero-word{
  position:absolute; inset:0; z-index:1; display:flex; flex-direction:column;
  align-items:center; justify-content:center; text-align:center; gap:1.1rem;
  padding:0 1.2rem 3.2rem;
  opacity:0;
  /* extra definition against the photo beyond what the vignette overlay
     already gives */
  filter:drop-shadow(0 2px 4px rgba(0,0,0,.6)) drop-shadow(0 14px 32px rgba(0,0,0,.4));
  /* the text layer fades in once script.js adds .text-in to .hero-stage
     (after the photo has finished expanding — see .hero-panel below).
     A JS-triggered transition, not a CSS @keyframes + animation-delay:
     iOS Safari can fail to fire a delay-based keyframe start correctly
     if layout reflows (font/image load, viewport/toolbar settling)
     land during that delay on a cold load, silently skipping straight
     to the end state. script.js strips this transition once the reveal
     finishes so its own later scroll-driven opacity writes (fading the
     text back out) apply instantly instead of easing on every frame. */
  transition:opacity .8s cubic-bezier(.2,.8,.2,1);
}
.hero-stage.text-in .hero-word{ opacity:1; }
@media (min-width:721px){
  .hero-word{ padding-bottom:clamp(5rem, 16vh, 9rem); }
}
.hero-word h1{
  font-family:var(--font-display);
  font-size:var(--fs-hero);
  font-weight:700;
  line-height:.86;
  letter-spacing:-.01em;
  color:var(--dusk-text);
  text-transform:uppercase;
}
.hero-word .line{display:block; overflow:hidden;}
.hero-word .line span{
  display:inline-block;
  transform:translateY(100%);
  /* rises in together with hero-word's opacity fade, via the same
     .text-in class toggle */
  transition:transform .9s cubic-bezier(.2,.8,.2,1);
}
.hero-word .line:nth-child(2) span{transition-delay:.1s;}
.hero-word .line:nth-child(3) span{transition-delay:.2s;}
.hero-stage.text-in .hero-word .line span{transform:translateY(0);}

/* ---------- intro veil ---------- */
.intro-veil{
  position:fixed; inset:0; z-index:200; background:#0a0a0a;
  transition:opacity .6s cubic-bezier(.4,0,.2,1), visibility 0s linear .6s;
}
.intro-veil.is-hidden{ opacity:0; visibility:hidden; pointer-events:none; }
@media (prefers-reduced-motion: reduce){
  .intro-veil{ display:none; }
}

.hero-sub{
  font-family:var(--font-display); font-style:italic; font-weight:400;
  font-size:clamp(1.1rem,2vw,1.5rem);
  color:color-mix(in srgb, var(--dusk-text) 82%, transparent);
}

.hero-panel{
  position:absolute; left:50%; top:50%;
  width:38vmin; height:38vmin;
  transform:translate(-50%,-50%) translateZ(0);
  z-index:0;
  overflow:hidden;
  /* no background / no radius: during the peek-a-boo the clip window
     must be invisible — you should see the photo rise into view, not
     the empty box it's rising into. */
}
/* peek-a-boo reveal: a centred SQUARE window; the photo starts pushed
   down out of it and rises up into view (peek-a-boo), then the square
   expands to full-bleed. The whole timeline is hand-driven frame by
   frame in script.js (runHeroIntro) — pixel-exact, so the phase-1 frame
   stays a true square on any viewport, which cross-unit CSS transitions
   (vmin -> vw/vh/%) can't be trusted to do. The rules here are only the
   rest state before it runs and the settled state after. */
.hero-panel .media-canvas{ position:absolute; inset:0; transform:translateY(90%) scale(1.05); }
.hero-stage.image-in .hero-panel{ width:100%; height:100%; border-radius:0; }
.hero-stage.intro-done .hero-panel .media-canvas{ transform:translateY(0) scale(1.02); }
.hero-panel::after{
  /* darkens the photo so the title/caption stay legible regardless of
     what's underneath — the old fill/stroke trick relied on the fill
     exactly matching a solid dusk background, which a photo can't do.
     Centered on where hero-word actually sits (vertically centered, a
     bit above true middle from its bottom padding) rather than just the
     top/bottom edges — a plain top+bottom gradient left the middle band,
     exactly where the text is, the least covered. */
  content:''; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:
    radial-gradient(ellipse 65% 55% at 50% 46%, rgba(0,0,0,.6), rgba(0,0,0,.32) 55%, rgba(0,0,0,0) 85%),
    linear-gradient(180deg, rgba(0,0,0,.45), rgba(0,0,0,.1) 24%, rgba(0,0,0,.1) 72%, rgba(0,0,0,.55));
  /* held off during the peek-a-boo (it would show as a dark wash in the
     empty clip window); fades in only as the panel expands to full-bleed */
  opacity:0; transition:opacity .6s ease;
}
.hero-stage.image-in .hero-panel::after{ opacity:1; }
.hero-panel .panel-caption{
  position:absolute; left:1.4rem; right:1.4rem; bottom:1.4rem; z-index:1;
  display:flex; justify-content:space-between; align-items:baseline;
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.1em;
  text-transform:uppercase; color:#fff;
  text-shadow:0 1px 3px rgba(0,0,0,.7);
  opacity:0;
  transition:opacity .8s cubic-bezier(.2,.8,.2,1);
}
.hero-stage.text-in .panel-caption{ opacity:1; }

.scroll-cue{
  position:absolute; left:50%; bottom:2.2rem; z-index:2; transform:translateX(-50%);
  display:flex; flex-direction:column; align-items:center; gap:.5rem;
  font-family:var(--font-mono); font-size:.68rem; letter-spacing:.18em;
  text-transform:uppercase; color:var(--dusk-text);
  text-shadow:0 1px 3px rgba(0,0,0,.7);
  /* fades in with the rest of the text layer once the photo has filled */
  opacity:0; transition:opacity .8s cubic-bezier(.2,.8,.2,1);
}
.hero-stage.text-in .scroll-cue{ opacity:1; }
.scroll-cue .stem{width:1px;height:34px;background:currentColor;overflow:hidden;position:relative;}
.scroll-cue .stem i{position:absolute;top:-100%;left:0;width:100%;height:100%;background:var(--accent);animation:drop 1.8s ease-in-out infinite;}
@keyframes drop{ 0%{top:-100%;} 60%{top:100%;} 100%{top:100%;} }
@media (prefers-reduced-motion: reduce){
  .hero-pin{ height:100vh; height:100svh; }
  .hero-stage{ position:relative; }
  .hero-panel{ top:50%; width:100%; height:100%; border-radius:0; transform:translate(-50%,-50%); transition:none; }
  .hero-panel .media-canvas img{ transform:scale(1.02); transition:none; }
  .hero-word{ transform:none; opacity:1; transition:none; }
  .hero-word .line span{ transform:translateY(0); transition:none; }
  .hero-panel .panel-caption{ opacity:1; transition:none; }
  .scroll-cue{ opacity:1; }
  .scroll-cue .stem i{animation:none;top:0;}
}

/* ---------- snap viewport ----------
   The ONE scroll context on desktop: hero + feed + footer all live in
   this single fixed-height box (the document itself does not scroll).
   That's the fix for the "hero freezes at the top, sections cut off at
   the bottom" bug — it was caused by the hero-pin sitting in the
   document scroll while the feed sat in this nested scroller, two
   contexts that could desync on a fast flick.

   Section snapping is pure native CSS scroll-snap — no JS wheel handler
   (see the note in script.js for why every JS version misbehaved).
   `mandatory` keeps it from ever resting off-section; with
   `scroll-snap-stop:always` on each section, one wheel gesture can't
   cross more than a single section however hard it's flicked. Nothing
   (no scrollTo({smooth}), no rAF glide) runs alongside it, so there's
   nothing to produce the old "lands, then clicks into place" settle.
   Mobile (below) drops the nested container and uses document-level snap. */
.snap-viewport{
  height:100vh; height:100svh;
  overflow-y:auto; overflow-x:hidden;
  scroll-snap-type:y mandatory;
}
.snap-viewport > #hero,
.snap-viewport > .feed > .feed-intro,
.snap-viewport > .feed > .post{
  scroll-snap-align:start; scroll-snap-stop:always;
}
/* Footer is shorter than a slide. Align to the end of the scrollport so
   a downward flick from the last module parks on the compact CTA, and
   an upward flick snaps back to that last module. `start` would be
   unreachable (not enough leftover scroll to put the footer at the top). */
.snap-viewport > .site-footer{
  scroll-snap-align:end; scroll-snap-stop:always;
}
@media (max-width:720px){
  /* Mobile can't use the desktop mechanics as-is: a nested scroll
     container + position:sticky + the iOS dynamic toolbar is a
     long-documented weak spot (pinned hero detaching, horizontal drag
     leaking through overflow:clip). So mobile keeps the plain document
     flow — no nested scroller — but still gets both effects:
       - the hero is a normal full-height section (its load-in
         peek-a-boo still runs, driven by rAF in script.js — that part
         never depended on the scroll pin), just with no scroll-scrub.
       - section snapping is done with document-level CSS scroll-snap
         instead of the desktop JS glide. scroll-snap on the ROOT
         scroller (not a nested box) is the combination iOS handles
         reliably. */
  .snap-viewport{ height:auto; overflow:visible; }
  .hero-pin{ height:100svh; }
  .hero-stage{ position:relative; }
  /* HOMEPAGE ONLY. `html` is the scroll container on mobile, so an
     unscoped `scroll-snap-type` here hijacked every route — on a work
     sub-page the only snap-aligned element is the footer, so `mandatory`
     snap dragged you to the bottom and refused to let you scroll back
     up. `:has(.snap-viewport)` is true only on the homepage; the
     snap-align rule is likewise scoped to that wrapper's descendants so
     a sub-page footer is never a snap target. */
  html:has(.snap-viewport){ scroll-snap-type:y mandatory; }
  .snap-viewport #hero,
  .snap-viewport .feed-intro,
  .snap-viewport .post{
    scroll-snap-align:start; scroll-snap-stop:always;
  }
  .snap-viewport .site-footer{
    scroll-snap-align:end; scroll-snap-stop:always;
  }
}

.feed{ position:relative; background:var(--paper); z-index:1; }
.feed-intro{
  min-height:100vh; min-height:100svh;
  display:flex; flex-direction:column; justify-content:center;
  max-width:70rem; margin:0 auto; padding:clamp(1.2rem,4vw,2.4rem);
  text-align:left;
}
.feed-intro h2{
  font-size:var(--fs-display); margin-top:.6rem; line-height:1.08;
  letter-spacing:-.02em; text-wrap:pretty;
}
.feed-intro-line{ display:block; }
.feed-intro p{margin-top:1.1rem; color:var(--ink-soft); max-width:34em;}

/* every module occupies exactly one viewport's height, regardless of
   layout, so they're uniform and line up cleanly with snap-scrolling */
.post{
  position:relative; box-sizing:border-box;
  min-height:100vh; min-height:100svh;
  display:flex; flex-direction:column;
  margin:0 auto; max-width:1400px;
  padding:clamp(5.5rem,12vh,7.5rem) clamp(1rem,3vw,1.6rem) clamp(1.5rem,4vh,2.5rem);
}
.post-media{ position:relative; width:100%; overflow:hidden; background:var(--dusk); }

/* media-row: the flex row of images/videos for multi-up modules (or the
   sole wrapper for single-image ones). Kept separate from .post-caption
   (a normal block sibling) so caption text never competes with the
   images for row width, and sized to fill whatever space the caption
   below it doesn't need — this is what makes every module the same
   total height no matter how many images it holds. */
.media-row{ flex:1 1 auto; min-height:0; display:flex; gap:clamp(.5rem,1.2vw,.9rem); }
.media-row .post-media{ flex:1 1 0; min-width:0; }

/* full-bleed: edge-to-edge AND full-height — the image fills the whole
   viewport (no header gap, no bottom band), and the caption overlays the
   bottom on a scrim instead of taking flow space below it */
.post.bleed{ max-width:none; padding:0; }
.media-row.bleed{ gap:0; }
.post.bleed .post-caption{
  position:absolute; left:0; right:0; bottom:0; z-index:3;
  width:auto; margin:0;
  padding:2.4rem clamp(1rem,3vw,1.6rem) 1.2rem;
  color:#fff;
  background:linear-gradient(180deg, rgba(0,0,0,0), rgba(0,0,0,.55));
}
.post.bleed .cap-tag{ color:rgba(255,255,255,.72); }
.post.bleed .cap-rule{ border-top-color:rgba(255,255,255,.4); }

/* contained asymmetric split */
.media-row.asym .post-media:first-child{ flex:0 1 38%; }
.media-row.asym .post-media:last-child{ flex:1 1 62%; }

/* framed single: narrower, more surrounding whitespace */
.post.framed{ max-width:760px; }

.media-canvas{ position:absolute; inset:0; background-size:cover; background-position:center; }
.media-canvas img, .media-canvas video{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:center; display:block; }
.post-media > video{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:center; display:block; }
.media-canvas::after{
  content:''; position:absolute; inset:0; background-image:var(--noise);
  opacity:.28; mix-blend-mode:overlay; pointer-events:none;
}
.post-mark{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  font-family:var(--font-display); font-weight:500; text-transform:uppercase;
  font-size:clamp(1.4rem,4vw,2.6rem); color:#fff; letter-spacing:.02em;
  text-align:center; padding:0 1rem; text-wrap:balance; opacity:.92;
}
.play-chip{
  position:absolute; right:1.2rem; top:1.2rem; width:2.4rem; height:2.4rem; border-radius:50%;
  background:rgba(255,255,255,.14); backdrop-filter:blur(6px);
  display:flex; align-items:center; justify-content:center;
}
.play-chip::before{ content:''; border-style:solid; border-width:.42rem 0 .42rem .7rem; border-color:transparent transparent transparent #fff; margin-left:2px;}
.anim-drift{ background-size:220% 220%; animation:drift 14s ease-in-out infinite alternate; }
@keyframes drift{ from{background-position:20% 30%;} to{background-position:80% 70%;} }
@media (prefers-reduced-motion: reduce){ .anim-drift{animation:none;} }

.post-caption{
  display:flex; align-items:baseline; gap:.9rem; margin-top:.85rem;
  font-family:var(--font-mono); font-size:var(--fs-cap); letter-spacing:.06em;
}
/* FEED MODULES ONLY: the caption spans the full viewport with the same
   gutter, whatever the image width above it — a consistent baseline
   across contained, framed and full-bleed modules. Scoped to .feed so it
   never touches captions inside the work grid or case-study grids. */
.feed .post-caption{
  width:100vw; margin-left:calc(50% - 50vw);
  padding-inline:clamp(1rem,3vw,1.6rem);
}
.cap-client{ text-transform:uppercase; white-space:nowrap; }
.cap-rule{
  flex:1; align-self:center; height:0;
  border-top:3px dotted var(--line); /* bigger dots between client and tag */
}
.cap-tag{ color:var(--ink-soft); text-transform:uppercase; white-space:nowrap; }
.cap-case{
  background:none;border:0;font:inherit;letter-spacing:inherit;color:var(--accent-strong);
  text-transform:uppercase; white-space:nowrap; cursor:pointer; padding:0;
}
.cap-case:hover{ text-decoration:underline; }

.tone-a .media-canvas{ background-image:linear-gradient(135deg,#003D30,#D5F6B4 130%); }
.tone-b .media-canvas{ background-image:linear-gradient(160deg,#22403f,#0d1a19); }
.tone-c .media-canvas{ background-image:linear-gradient(150deg,#3a2a2e,#7a4b46); }
.tone-d .media-canvas{ background-image:linear-gradient(140deg,#dcd6c4,#a99c81); }
.tone-e .media-canvas{ background-image:linear-gradient(145deg,#1b2233,#47597a); }
.tone-f .media-canvas{ background-image:linear-gradient(155deg,#292520,#6a5637); }
.tone-g .media-canvas{ background-image:linear-gradient(150deg,#1a1023,#6b3fa0 130%); }
.tone-d .post-mark{ color:#241f16; }

/* ---------- fixed-ratio treatments ---------- */
.v9x16{ aspect-ratio:9/16; }
.v4x5{ aspect-ratio:4/5; }
.v5x4{ aspect-ratio:5/4; }

/* video-pair shares aspect-fit sizing (JS fits 9:16 tiles in the
   leftover box). Keep the class for case-study chrome. */
.media-row.video-pair{ justify-content:center; }

/* Fixed-ratio rows (4×5 photo pair, mixed pair, 4×5 video 2-up/3-up):
   keep the crop, don't stretch to fill. Tiles are centered in the leftover
   viewport above the caption; pixel size is set in JS (fixAspectPairSize). */
.media-row.aspect-fit{
  justify-content:center;
  align-items:center;
}
.media-row.aspect-fit .post-media{
  flex:0 0 auto;
  width:auto;
  height:100%;
  max-height:100%;
}

/* click-to-play: poster shown until tapped, then plays with sound */
.play-btn{
  position:absolute; inset:0; margin:auto; width:4.2rem; height:4.2rem; border-radius:50%;
  background:rgba(0,0,0,.55); backdrop-filter:blur(4px);
  display:flex; align-items:center; justify-content:center;
  border:0; cursor:pointer; padding:0; z-index:2;
  transition:background .2s ease, transform .2s ease;
}
.play-btn:hover{ background:rgba(0,0,0,.75); transform:scale(1.06); }
.play-btn::before{ content:''; border-style:solid; border-width:.6rem 0 .6rem 1rem; border-color:transparent transparent transparent #fff; margin-left:4px; }
.play-btn.is-hidden{ display:none; }

.reveal{ opacity:0; transform:translateY(28px); transition:opacity .8s cubic-bezier(.2,.7,.2,1), transform .8s cubic-bezier(.2,.7,.2,1); }
.reveal.is-visible{ opacity:1; transform:none; }
@media (prefers-reduced-motion: reduce){ .reveal{opacity:1;transform:none;transition:none;} }

/* ---------- footer / CTA ----------
   Compact band, not a full-viewport slide. On the homepage it sits
   after the last feed module: scroll down from that module to reveal
   it, scroll up to snap back. Sub-pages (footer is a direct child of
   <main>) keep the same short band — a 100vh footer there caused an
   iOS svh reflow that stuck the scroll. */
.site-footer{
  position:relative; background:var(--dusk); color:var(--dusk-text);
  display:flex; flex-direction:column; justify-content:flex-start;
  gap:clamp(1.6rem,4vw,2.4rem);
  padding:clamp(2.2rem,5vw,3.2rem) clamp(1.2rem,4vw,2.4rem) 1.4rem;
  isolation:isolate;
}
main > .site-footer{ min-height:0; }
.site-footer::before{
  content:''; position:absolute; inset:0; background-image:var(--noise);
  opacity:.4; mix-blend-mode:overlay; pointer-events:none;
}
.footer-cta{ max-width:900px; }
.footer-cta .eyebrow{ color:var(--accent); }
.footer-cta h2{ font-size:clamp(2.2rem,6vw,4.4rem); margin-top:.8rem; line-height:1.05; }
.footer-email{
  display:inline-block; margin-top:1.2rem; font-family:var(--font-display); font-style:italic;
  font-size:clamp(.95rem,1.4vw,1.15rem); text-decoration:none; border-bottom:1px solid var(--accent);
  padding-bottom:.15rem;
}
.footer-bottom{
  display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:1rem 2rem;
  margin-top:0; padding-top:1.2rem; border-top:1px solid var(--line);
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.08em; text-transform:uppercase; color:var(--dusk-text-soft);
}
.footer-bottom > :first-child{ justify-self:start; min-width:0; }
.footer-bottom > :last-child:not(.nav-instagram){ justify-self:end; text-align:right; min-width:0; }
.footer-bottom a{ color:var(--dusk-text); text-decoration:none; }
.footer-bottom a:hover{ text-decoration:underline; }
.footer-bottom a.nav-instagram{
  display:inline-flex; align-items:center; line-height:0;
  grid-column:2; justify-self:center;
}
.footer-bottom a.nav-instagram:hover{ text-decoration:none; opacity:.75; }
.footer-bottom a.nav-instagram svg{ width:1.15rem; height:1.15rem; display:block; }

/* ---------- services drawer ---------- */
.backdrop{
  position:fixed; inset:0; z-index:70; background:rgba(10,10,12,.42); backdrop-filter:blur(3px);
  opacity:0; pointer-events:none; transition:opacity .4s ease;
}
body.services-open .backdrop, body.case-open .backdrop, body.menu-open .backdrop{ opacity:1; pointer-events:auto; }

.drawer{
  position:fixed; top:0; right:0; height:100%; width:min(560px,100%); z-index:80;
  background:var(--paper); color:var(--ink);
  transform:translateX(100%); transition:transform .55s cubic-bezier(.2,.8,.2,1), box-shadow .55s ease;
  display:flex; flex-direction:column; overflow-y:auto; overflow-x:hidden;
  pointer-events:none;
}
body.services-open .drawer{
  transform:translateX(0);
  box-shadow:-30px 0 60px -20px rgba(0,0,0,.35);
  pointer-events:auto;
}
.drawer-inner{ padding:clamp(5.5rem,9vw,6.5rem) clamp(1.6rem,4vw,3rem) 3rem; flex:1; }
.drawer-head{ display:flex; justify-content:space-between; align-items:flex-start; }
.drawer-head h2{ font-size:var(--fs-display); margin-top:.5rem; }

.icon-close{
  width:2.6rem; height:2.6rem; border-radius:50%; border:1px solid var(--line);
  background:none; cursor:pointer; position:relative; flex:none;
}
.icon-close::before, .icon-close::after{
  content:''; position:absolute; left:50%; top:50%; width:1rem; height:1px; background:var(--ink);
}
.icon-close::before{ transform:translate(-50%,-50%) rotate(45deg); }
.icon-close::after{ transform:translate(-50%,-50%) rotate(-45deg); }
.icon-close:hover{ border-color:var(--accent-strong); }

.service-list{ margin-top:2.6rem; display:flex; flex-direction:column; }
.service-row{ padding:1.5rem 0; border-top:1px solid var(--line); }
.service-list .service-row:last-child{ border-bottom:1px solid var(--line); }
.service-row h3{ font-size:var(--fs-h3); }
.service-row h3 a{ text-decoration:none; }
.service-row h3 a:hover{ text-decoration:underline; }
.service-row p{ margin-top:.5rem; color:var(--ink-soft); max-width:38em; }

.drawer-foot{ margin-top:2.6rem; }
.drawer-foot a{ font-family:var(--font-mono); font-size:var(--fs-nav); letter-spacing:.08em; text-transform:uppercase; text-decoration:none; color:var(--accent-strong); }
.drawer-foot a:hover{ text-decoration:underline; }

/* ---------- about / case overlays (full screen) ---------- */
.overlay{
  position:fixed; inset:0; z-index:80; background:var(--paper); color:var(--ink);
  opacity:0; visibility:hidden; transform:translateY(18px);
  transition:opacity .5s ease, transform .5s cubic-bezier(.2,.8,.2,1), visibility 0s linear .5s;
  overflow-y:auto; overflow-x:hidden;
}
/* When closed the overlay must never intercept input. visibility:hidden
   alone isn't enough on iOS Safari — a fixed, full-viewport element there
   can still swallow touchmove, which on a sub-page (document scroll, not
   the .snap-viewport) makes the page feel stuck. */
.overlay:not(.is-open){ pointer-events:none; }
.overlay.is-open{ opacity:1; visibility:visible; transform:none; pointer-events:auto; transition:opacity .5s ease, transform .5s cubic-bezier(.2,.8,.2,1); }
.overlay-inner{ max-width:760px; margin:0 auto; padding:clamp(6rem,11vw,8rem) clamp(1.2rem,4vw,1.6rem) 5rem; }
.overlay .icon-close{ position:fixed; top:1.6rem; right:clamp(1.2rem,3vw,2.4rem); z-index:90; background:var(--paper); }

/* page overlay: About/Work/Contact/services slide over the homepage
   instead of navigating to a separate page. The fetched page's own
   .page-hero/.page-content/.work-grid already manage their own width
   and top clearance for the fixed header, so this wrapper stays plain. */
.page-overlay-inner:empty{ min-height:60vh; }

.overlay:has(.about-page) .page-overlay-inner,
.overlay .about-page{ min-height:100%; }

.case-cover{ width:100%; aspect-ratio:16/9; position:relative; overflow:hidden; margin-top:2rem; }
.case-meta{ display:flex; flex-wrap:wrap; gap:.6rem 1.6rem; margin-top:1.6rem; font-family:var(--font-mono); font-size:var(--fs-cap); letter-spacing:.08em; text-transform:uppercase; color:var(--ink-soft); }
.case-dek{ margin-top:1.4rem; font-size:1.15rem; color:var(--ink-soft); max-width:36em; }
.case-body p{ margin-top:1.2rem; color:var(--ink-soft); max-width:38em; }
/* Case studies need more room than contact/services so the
   2-up gallery can breathe. Mobile is unchanged — max-width
   only binds once the viewport exceeds the cap. */
.case-page .page-hero,
.case-page .page-content{ max-width:1200px; }
.case-page .page-content p,
.case-page .case-body p{ max-width:none; }
.case-gallery{
  margin-top:2.4rem;
  display:flex;
  flex-direction:column;
  gap:1.6rem;
}
/* Case-study modules reuse homepage layout classes but not the 100vh snap
   height — they stack in document flow. Tile crop comes from aspect-ratio. */
.case-gallery .post{
  min-height:0;
  height:auto;
  padding:0;
  max-width:none;
  margin:0;
}
.case-gallery .media-row{
  flex:none;
  min-height:0;
}
.case-gallery .post.framed{ max-width:760px; margin-inline:auto; }
.case-gallery .post.bleed{
  width:100vw;
  max-width:none;
  margin-left:calc(50% - 50vw);
}
.case-gallery .post-media{ aspect-ratio:4/5; }
.case-gallery .post-media.v4x5{ aspect-ratio:4/5; }
.case-gallery .post-media.v5x4{ aspect-ratio:5/4; }
.case-gallery .post-media.v9x16{ aspect-ratio:9/16; }
.case-gallery .media-row:not(.split):not(.trio):not(.asym):not(.bleed):not(.video-pair):not(.aspect-fit) .post-media{
  aspect-ratio:16/9;
}
.case-gallery .media-row.bleed .post-media:only-child{ aspect-ratio:16/9; }
.case-gallery .media-row.video-pair,
.case-gallery .media-row.aspect-fit{ height:auto; }
.case-gallery .media-row.video-pair .post-media,
.case-gallery .media-row.aspect-fit .post-media{
  flex:1 1 0;
  width:auto;
  height:auto;
  max-height:none;
}
/* pair-mixed-even: same 4×5 + 5×4 crops as pair-mixed, but column
   widths follow the ratios (4/5 : 5/4 = 16:25) so both tiles share
   one height. pair-mixed stays equal-width / uneven-height. */
.case-gallery .media-row.pair-mixed-even{
  align-items:flex-start;
}
.case-gallery .media-row.pair-mixed-even .post-media.v4x5{ flex:16 1 0; }
.case-gallery .media-row.pair-mixed-even .post-media.v5x4{ flex:25 1 0; }
/* Case-study 9:16 video-pair tiles sit on the page surface. The shared
   .post-media dusk fill reads as a green matte around posters that don't
   fully cover. Round the tile so the square box's corners don't peek out
   past the phone-mockup posters; video chrome is black by default.
   4×5 video modules keep square corners like the photo pair layouts. */
.case-gallery .media-row.video-pair .post-media{
  background:transparent;
  border-radius:1.5rem;
}
.case-gallery .post-media > video,
.case-gallery .video-poster{
  background:transparent;
}
/* Screenshot posters are phone mockups on a black field with extra
   padding at the bottom. Zoom from above-center so the crop is even;
   the playing <video> stays unscaled (full-bleed). 4×5 posters are the
   actual frame, so they skip the mockup zoom. */
.video-poster{ display:none; }
.case-gallery .video-poster{
  display:block; position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; object-position:center;
}
.case-gallery .media-row.video-pair .video-poster{
  transform:scale(1.13);
  transform-origin:50% 34%;
}
.case-gallery .post-media:not(.is-playing) > video{ opacity:0; }
.case-gallery .post-media.is-playing .video-poster{ display:none; }
.case-credits{ margin-top:2.4rem; padding-top:1.4rem; border-top:1px solid var(--line); font-family:var(--font-mono); font-size:var(--fs-cap); color:var(--ink-soft); letter-spacing:.04em; }
.case-nav{
  margin-top:3.5rem; padding-top:1.6rem; border-top:1px solid var(--line);
  display:flex; align-items:baseline; justify-content:space-between; gap:1rem 1.6rem; flex-wrap:wrap;
  font-family:var(--font-mono); font-size:var(--fs-cap); letter-spacing:.08em; text-transform:uppercase;
}
.case-nav a{ text-decoration:none; color:var(--ink); }
.case-nav a:hover{ color:var(--accent-strong); }
.case-nav .case-nav-all{ color:var(--ink-soft); }
.case-nav .case-nav-next{ margin-left:auto; text-align:right; }
@media (max-width:560px){
  .case-nav{ flex-direction:column; align-items:stretch; text-align:left; }
  .case-nav .case-nav-next{ margin-left:0; text-align:left; }
}

/* ---------- standalone pages (about / work / contact / service detail) ---------- */
.page-hero{
  padding:clamp(7.5rem,14vw,10rem) clamp(1.2rem,4vw,2.4rem) clamp(2rem,5vw,3rem);
  max-width:900px; margin:0 auto;
}
.page-hero h1{ font-size:var(--fs-display); margin-top:.8rem; }
.page-hero p.dek{ margin-top:1.2rem; font-size:1.15rem; color:var(--ink-soft); max-width:38em; }

.page-content{ max-width:760px; margin:0 auto; padding:0 clamp(1.2rem,4vw,2.4rem) clamp(5rem,10vw,7rem); }
.page-content p{ margin-top:1.2rem; color:var(--ink-soft); max-width:40em; }
.page-content p.lede{ font-size:1.2rem; color:var(--ink); }
.page-content h2{ font-size:var(--fs-h3); margin-top:3rem; }

.pull-quote{
  margin:3rem 0; padding:2rem 0; border-top:1px solid var(--line); border-bottom:1px solid var(--line);
}
.pull-quote p{ font-family:var(--font-display); font-style:italic; font-size:1.4rem; color:var(--ink); margin:0; }
.pull-quote cite{ display:block; margin-top:1rem; font-family:var(--font-mono); font-size:var(--fs-cap); letter-spacing:.08em; text-transform:uppercase; font-style:normal; color:var(--ink-soft); }

.detail-grid{ margin-top:1.5rem; display:grid; gap:1.6rem; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); }
.detail-item h3{ font-family:var(--font-mono); font-size:.72rem; letter-spacing:.08em; text-transform:uppercase; color:var(--accent-strong); font-weight:500; }
.detail-item p{ margin-top:.5rem; }

/* ---------- about page ---------- */
main:has(.about-page){ background:var(--paper); }
.about-page{
  background:var(--paper); color:var(--ink);
  padding:clamp(7.2rem, 13vw, 9.5rem) 0 clamp(4.5rem, 9vw, 6.5rem);
  --about-line: var(--line);
  --about-muted: var(--ink-soft);
}
.about-inner{
  max-width:1180px;
  margin:0 auto;
  padding:0 clamp(1.4rem, 4.5vw, 3.2rem);
}
.about-page .eyebrow{
  color:var(--accent-strong);
  letter-spacing:.16em;
  font-size:.68rem;
}
.about-kicker{ display:block; margin-bottom:1.1rem; }
.about-intro{
  display:grid;
  grid-template-columns:minmax(0, 1.45fr) minmax(15rem, .72fr);
  gap:clamp(2.2rem, 6vw, 5.5rem);
  align-items:start;
}
.about-intro-main h1{
  font-size:clamp(2.35rem, 4.8vw, 4.05rem);
  font-weight:500;
  line-height:1.08;
  letter-spacing:-.03em;
  color:var(--ink);
  text-wrap:pretty;
}
.about-intro-main h1 em{
  font-style:italic;
  font-weight:400;
  color:var(--accent-strong);
}
.about-intro-main p{
  margin-top:1.35rem;
  max-width:36em;
  font-size:clamp(.98rem, .2vw + .92rem, 1.08rem);
  line-height:1.65;
  color:var(--about-muted);
}
.about-aside{
  padding-top:.35rem;
  display:flex;
  flex-direction:column;
  gap:0;
}
.about-aside-block{
  padding:1.45rem 0 1.55rem;
  border-top:1px solid var(--about-line);
}
.about-aside-block:first-child{ padding-top:0; border-top:0; }
.about-aside-block p,
.about-aside-list{
  margin-top:.7rem;
  font-size:.92rem;
  line-height:1.55;
  color:var(--about-muted);
}
.about-aside-list{ list-style:none; padding:0; }
.about-aside-list li + li{ margin-top:.28rem; }
.about-aside-list a{ text-decoration:none; color:inherit; }
.about-aside-list a:hover{ color:var(--ink); text-decoration:underline; }

.about-quote{
  display:grid;
  grid-template-columns:auto 1fr;
  gap:clamp(1.4rem, 3.8vw, 2.6rem);
  align-items:center;
  margin:clamp(3.2rem, 7vw, 5rem) 0 0;
  padding:clamp(2.4rem, 5vw, 3.2rem) 0;
  border-top:1px solid var(--about-line);
  border-bottom:1px solid var(--about-line);
}
.about-quote + .about-method,
.about-quote + .about-caps,
.about-quote + .about-text,
.about-quote + .about-quote{
  margin-top:0;
  border-top:none;
}
.about-quote:not(:has(img)){ grid-template-columns:1fr; }
.about-quote img{
  width:clamp(7.4rem, 13vw, 10.4rem);
  height:clamp(7.4rem, 13vw, 10.4rem);
  border-radius:50%;
  object-fit:cover;
  object-position:center;
}
.about-quote blockquote{ margin:0; }
.about-quote p{
  margin:0;
  font-family:var(--font-display);
  font-style:italic;
  font-weight:400;
  font-size:clamp(1.35rem, 2.35vw, 2.05rem);
  line-height:1.32;
  letter-spacing:-.02em;
  color:var(--ink);
  text-wrap:pretty;
}
.about-quote cite{
  display:block;
  margin-top:1.05rem;
  font-family:var(--font-mono);
  font-size:.68rem;
  letter-spacing:.12em;
  text-transform:uppercase;
  font-style:normal;
  color:var(--about-muted);
}

.about-method,
.about-caps{
  margin-top:clamp(3.2rem, 7vw, 5rem);
  padding-top:1.7rem;
  border-top:1px solid var(--about-line);
}
.about-method-grid{
  display:grid;
  grid-template-columns:repeat(3, minmax(0, 1fr));
  gap:clamp(1.6rem, 3.5vw, 2.8rem);
  margin-top:1.7rem;
}
.about-method-num{
  display:block;
  font-family:var(--font-mono);
  font-size:.68rem;
  letter-spacing:.14em;
  color:var(--about-muted);
}
.about-method-col h2{
  margin-top:.55rem;
  font-size:clamp(1.45rem, 2vw, 1.85rem);
  font-weight:500;
  letter-spacing:-.02em;
  color:var(--accent-strong);
}
.about-method-col p{
  margin-top:.85rem;
  font-size:.92rem;
  line-height:1.62;
  color:var(--about-muted);
}

.about-caps-grid{
  margin-top:1.8rem;
  display:grid;
  grid-template-columns:repeat(3, minmax(0, 1fr));
  gap:clamp(1.6rem, 3vw, 2.4rem) clamp(1.8rem, 3.5vw, 2.8rem);
}
.about-caps-item dt{
  font-family:var(--font-display);
  font-size:1.2rem;
  font-weight:500;
  color:var(--accent-strong);
}
.about-caps-item dd{
  margin:.45rem 0 0;
  font-size:.9rem;
  line-height:1.55;
  color:var(--about-muted);
  overflow-wrap:break-word;
}

.about-text{
  margin-top:clamp(3.2rem, 7vw, 5rem);
  padding-top:1.7rem;
  border-top:1px solid var(--about-line);
}
.about-text h2{
  margin-top:.55rem;
  font-size:clamp(1.45rem, 2vw, 1.85rem);
  font-weight:500;
  letter-spacing:-.02em;
  color:var(--accent-strong);
}
.about-text p{
  margin-top:.85rem;
  max-width:40em;
  font-size:.98rem;
  line-height:1.65;
  color:var(--about-muted);
}
.about-inner > :first-child{
  margin-top:0;
  border-top:none;
  padding-top:0;
}

@media (max-width:860px){
  .about-intro{ grid-template-columns:1fr; gap:2.4rem; }
  .about-method-grid,
  .about-caps-grid{ grid-template-columns:1fr; }
}
@media (max-width:640px){
  .about-quote{ grid-template-columns:1fr; justify-items:start; }
}
@media (min-width:861px) and (max-width:1040px){
  .about-caps-grid{ grid-template-columns:repeat(2, minmax(0, 1fr)); }
}

/* work grid */
.work-grid{
  max-width:1400px; margin:0 auto; padding:0 clamp(1rem,3vw,1.6rem) clamp(5rem,10vw,7rem);
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:clamp(.6rem,1.4vw,1rem);
}
.work-cell{ position:relative; min-width:0; }
.work-cell .post-media{ aspect-ratio:1/1; }
.work-cell .post-caption{ margin-top:.6rem; }
/* the grid caption stands alone (no dotted rule beside it) — let a long
   client name wrap instead of blowing its column wider than 1fr, which
   left the 2-up mobile grid with mismatched, misaligned tiles */
.work-cell .cap-client{ white-space:normal; }
/* The View control fills the whole tile so a tap anywhere on the image
   navigates (matters on touch, where there's no hover). The literal
   link text is hidden; the visible pill is drawn with ::after and only
   fades in on hover, so on mobile the tile just looks like a plain image. */
.work-cell .view-btn{
  position:absolute; inset:0;
  font-size:0; text-decoration:none;
}
.work-cell .view-btn::after{
  content:'View';
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  font-family:var(--font-mono); font-size:.7rem; line-height:1; letter-spacing:.1em;
  text-transform:uppercase; color:#fff; white-space:nowrap;
  padding:.55rem 1.4rem; border:1px solid #fff; border-radius:999px;
  background:rgba(0,0,0,.15); backdrop-filter:blur(2px);
  opacity:0; transition:opacity .25s ease;
}
.work-cell:hover .view-btn::after{ opacity:1; }

/* contact form */
.contact-form{ margin-top:2.4rem; display:flex; flex-direction:column; gap:1.8rem; }
.form-row{ display:grid; gap:1.2rem; grid-template-columns:1fr 1fr; }
.form-field label{
  display:block; font-family:var(--font-mono); font-size:.72rem; letter-spacing:.08em;
  text-transform:uppercase; color:var(--ink-soft); margin-bottom:.5rem;
}
.form-field input, .form-field textarea{
  width:100%; padding:.8rem .9rem; border:1px solid var(--line); background:var(--paper-soft);
  color:var(--ink); font-family:var(--font-body); font-size:1rem; border-radius:2px;
  color-scheme:inherit; accent-color:var(--accent-strong);
}
.form-field textarea{ min-height:140px; resize:vertical; }
.form-check{ display:flex; align-items:center; gap:.6rem; font-size:.9rem; color:var(--ink-soft); }
.form-check input{ width:1rem; height:1rem; accent-color:var(--accent-strong); color-scheme:inherit; }
.form-submit{
  align-self:flex-start; padding:.8rem 2rem; border:1px solid var(--ink); background:none;
  color:var(--ink); font-family:var(--font-mono); font-size:.78rem; letter-spacing:.08em;
  text-transform:uppercase; cursor:pointer; border-radius:999px; transition:background .2s ease,color .2s ease;
}
.form-submit:hover{ background:var(--ink); color:var(--paper); }
.form-submit:disabled{ opacity:.55; cursor:wait; }
.form-status.is-error{ margin:0; color:var(--ink); }
.contact-success{ margin-top:1.6rem; outline:none; }
.contact-success .lede{ margin-top:0; }
.page-content .contact-success p + p{ margin-top:.8rem; }

@media (max-width:720px){
  /* stacked rows still fill the module's full height between them —
     .media-row .post-media's flex:1 1 0 divides it evenly either way */
  .media-row.split{ flex-direction:column; }
  .media-row.bleed{ flex-direction:column; }
  /* Work pages stack 9:16 pairs at native ratio (document scroll).
     The feed keeps them in a row: a 100vh snap slide can't hold two
     full-width 9:16 tiles, and flex-stretching them made squares. */
  .case-gallery .media-row.video-pair{ flex-direction:column; }
  .case-gallery .media-row.video-pair .post-media{
    flex:none;
    width:100%;
    height:auto;
    aspect-ratio:9/16;
  }
  /* Work pages stack aspect-fit modules at each tile's ratio.
     Feed stays in a row so JS can keep the crop — stacking 3-up
     into the leftover viewport crushed the tiles into slivers. */
  .case-gallery .media-row.aspect-fit{ flex-direction:column; }
  .case-gallery .media-row.aspect-fit .post-media{
    flex:none;
    width:100%;
    height:auto;
    max-height:none;
  }
  .case-gallery .media-row.pair-mixed-even .post-media.v4x5,
  .case-gallery .media-row.pair-mixed-even .post-media.v5x4{
    flex:none;
    width:100%;
  }
  .media-row.asym{ flex-direction:column; }
  .media-row.asym .post-media:first-child, .media-row.asym .post-media:last-child{ flex:1 1 0; }
  /* Trio on the feed is still a 100vh flex child, so 62% width
     tiles would stretch to the leftover height (a sliver crop).
     Lock 4:5 like the work-page gallery and center in the slide. */
  .media-row.trio{
    overflow-x:auto; scroll-snap-type:x mandatory; padding-bottom:.4rem;
    align-items:center;
  }
  .media-row.trio .post-media{
    flex:0 0 62%; scroll-snap-align:start;
    aspect-ratio:4/5; height:auto; align-self:center;
  }
  /* 3-up 4×5 videos: same swipe row as the photo trio, so they
     don't shrink to three tiny tiles in the 100vh slide. */
  .feed .media-row.video-trio-4x5{
    overflow-x:auto; scroll-snap-type:x mandatory; padding-bottom:.4rem;
    align-items:center;
  }
  .feed .media-row.video-trio-4x5 .post-media{
    flex:0 0 62%; scroll-snap-align:start;
    width:auto; height:auto; max-height:none;
    aspect-ratio:4/5; align-self:center;
  }
  .drawer{ width:100%; }
  .work-grid{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  .form-row{ grid-template-columns:1fr; }
}
