/* ============================================================================
   IMAGERY RECIPE — generalized (Stage 2 proposal)
   markdimercurio.com · the one-off .qp-hero--towers recipe, generalized into a
   reusable component: 3 carriers × a token-driven treatment (per Joshua's knobs).
   Tone mapping (desat + duotone + black/white-point + swappable highlight) is done
   with SVG <filter>s (see the inline sprite on each page); grain/scrim/contrast are
   CSS layers. Per-image override = set the treatment class + a few var overrides.
   Spec: review/photo-stage2-recipe.md · PROPOSAL — not wired into live pages.
   ========================================================================== */

/* ---------- Carriers (orientation-correct) ---------- */
.qp-img{ position:relative; overflow:hidden; background:#0d1a2b; }
.qp-img > img{ display:block; width:100%; height:100%; object-fit:cover;
  filter: var(--duo, none) contrast(var(--contrast,1.08)); }
.qp-img::after{ content:""; position:absolute; inset:0; pointer-events:none;
  background-image:var(--grain-img); background-size:200px; opacity:var(--grain,.12); mix-blend-mode:overlay; }
/* scrim only where text/UI sits (set --scrim per carrier instance) */
.qp-img.has-scrim::before{ content:""; position:absolute; inset:0; pointer-events:none; z-index:1;
  background:var(--scrim, linear-gradient(180deg, rgba(11,17,28,.0) 40%, rgba(11,17,28,.72) 100%)); }

/* ---------- Drifting fog overlay (opt-in: add <span class="qp-fog"> inside a .qp-img) ----------
   Two slow, blurred, screen-blended light layers that drift in opposite directions read as
   moving mist over the photo. Looping — guarded by prefers-reduced-motion. */
.qp-fog{ position:absolute; inset:-16% -12%; z-index:1; pointer-events:none;
  background:
    radial-gradient(55% 38% at 28% 60%, rgba(208,221,238,.20), rgba(208,221,238,0) 70%),
    radial-gradient(46% 30% at 72% 38%, rgba(186,203,226,.15), rgba(186,203,226,0) 72%);
  filter:blur(9px); mix-blend-mode:screen; opacity:.92;
  animation:qpFogDrift 32s ease-in-out infinite alternate; }
.qp-fog::after{ content:""; position:absolute; inset:0;
  background:radial-gradient(58% 40% at 52% 82%, rgba(216,228,242,.13), rgba(216,228,242,0) 70%);
  animation:qpFogDrift2 23s ease-in-out infinite alternate; }
@keyframes qpFogDrift{ from{ transform:translate3d(-3%,1.5%,0) scale(1.06); } to{ transform:translate3d(5%,-2%,0) scale(1.15); } }
@keyframes qpFogDrift2{ from{ transform:translate3d(3%,-1%,0); opacity:.65; } to{ transform:translate3d(-4%,2%,0); opacity:1; } }
@media (prefers-reduced-motion: reduce){ .qp-fog, .qp-fog::after{ animation:none; } }

.qp-img--split{ aspect-ratio:3/4; border-radius:14px; }          /* portrait, text lives on a solid panel beside it */
.qp-img--band{ aspect-ratio:21/9; }                               /* landscape full-bleed */
.qp-img--inset{ aspect-ratio:4/5; max-width:360px; border-radius:12px; }  /* small, soft-res-tolerant */
@media (max-width:760px){ .qp-img--band{ aspect-ratio:16/10; } .qp-img--split{ aspect-ratio:4/5; } }

/* ---------- Layout wrappers (so the carriers can host real page sections) ---------- */
/* Split-hero: dark text panel beside the image. The existing hero copy moves into the panel. */
.qp-splithero{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.04fr); align-items:stretch;
  height:clamp(460px,58vh,560px); background:linear-gradient(180deg,#15233a,#0e1626); overflow:hidden; }
.qp-splithero .qp-sh-panel{ color:#fff; padding:clamp(36px,4.4vw,60px) clamp(26px,4.4vw,56px);
  display:flex; flex-direction:column; justify-content:center; }
/* tighter headline in the half-width panel so it doesn't wrap to a wall of text */
.qp-splithero .qp-sh-panel .hero-title{ font-size:clamp(30px,3.2vw,44px); line-height:1.06; letter-spacing:-1px; }
.qp-splithero .qp-sh-panel .hero-sub{ margin-top:16px; }
/* the panel is a column flexbox, which blockifies the inline-block eyebrow and stretches its
   border the full panel width. Pin it to its content so the box hugs the label (as on the home hero). */
.qp-splithero .qp-sh-panel .hero-eyebrow{ align-self:flex-start; }
/* image absolutely filled so its intrinsic portrait height can't push the grid row taller */
.qp-splithero .qp-img{ position:relative; aspect-ratio:auto; height:100%; border-radius:0; }
.qp-splithero .qp-img > img{ position:absolute; inset:0; object-position:center 42%; }
/* subtle Ken Burns drift on every split-hero photo — slow zoom + pan, motion-guarded */
@media (prefers-reduced-motion:no-preference){
  .qp-splithero .qp-img > img{ will-change:transform; animation:qpHeroKen 34s ease-in-out infinite alternate; }
}
@keyframes qpHeroKen{ from{ transform:scale(1.06) translate3d(-1.2%,-0.6%,0); } to{ transform:scale(1.11) translate3d(1.6%,1%,0); } }
@media (max-width:820px){ .qp-splithero{ grid-template-columns:1fr; height:auto; }
  .qp-splithero .qp-img{ order:-1; aspect-ratio:16/10; height:auto; }
  .qp-splithero .qp-img > img{ position:static; } }

/* Full-bleed band: edge-to-edge horizontal image, optional content overlaid. */
.qp-band{ position:relative; width:100%; overflow:hidden; }
.qp-band .qp-img{ border-radius:0; }
.qp-band .qp-band-over{ position:absolute; inset:0; z-index:2; display:flex; align-items:center; justify-content:center; }

/* grain noise source (CSP: img-src data: ok) */
:root{ --grain-img:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E"); }

/* ---------- Treatments (each = which SVG filter + its grain/contrast deviations) ----------
   Base default: full desat, navy→cool-white duotone, grain .12, contrast 1.08. Each image
   below lists ONLY its deviation from base (the override-layer contract). */
/* tone-mapping filters live in the shared external sprite assets/duotone-sprite.svg
   (same-origin external ref — CSP default-src 'self' allows it; no per-page inline sprite). */
.tone--base    { --duo:url(/assets/duotone-sprite.svg#duo-base);    --grain:.12; --contrast:1.08; }   /* fog-tower, spire-lloyds, canyon-gap */
.tone--premium { --duo:url(/assets/duotone-sprite.svg#duo-premium); --grain:.11; --contrast:1.12; }   /* converging-glints: white-point uncapped, glints survive */
.tone--mono    { --duo:grayscale(1);      --grain:.10; --contrast:1.13; }   /* bw-three: tint OFF — stays the one monochrome moment */
.tone--light   { --duo:url(/assets/duotone-sprite.svg#duo-light);   --grain:.06; --contrast:1.18; }   /* blue-glass: keep native blue, cap white, crisp */
.tone--balcony { --duo:url(/assets/duotone-sprite.svg#duo-balcony); --grain:.16; --contrast:1.20; }   /* balcony-sun: cap the sky, denser navy */
.tone--amber   { --duo:url(/assets/duotone-sprite.svg#duo-amber);   --grain:.08; --contrast:1.10; }   /* warm-windows: navy→amber, keep the glow */
.tone--dusk    { --duo:url(/assets/duotone-sprite.svg#duo-dusk);    --grain:.06; --contrast:1.06; }   /* dusk-tower: black-point lift so it isn't mud */
.tone--greenery{ --duo:url(/assets/duotone-sprite.svg#duo-light);  --grain:.16; --contrast:1.04; }   /* greenery: partial desat keeps a hint of green; heavy grain masks the soft res */
