/* ==========================================================================
   JI Edwards — supplementary styles
   Only what theme.json cannot express: component behaviour, hover states,
   grid layouts and a handful of utility classes used by the block patterns.
   ========================================================================== */

/* --------------------------------------------------------------- basics -- */

html { scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

body { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }

/* Border-box for everything this theme owns.
   Not a global reset — WordPress core blocks have their own expectations —
   but every jie-* component sets width:100% somewhere, and content-box
   silently adds the padding on top. That shipped twice: the contact form
   inputs overflowed by 7px, and the hero panel by exactly 48px (2 x the 24px
   root padding). Fixing it once at the root is better than chasing it. */
[class^="jie-"], [class*=" jie-"] { box-sizing: border-box; }

/* A visible focus ring everywhere. The default browser outline disappears
   against the cream ground, and keyboard users are the ones who need it. */
:where(a, button, input, textarea, select, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--wp--preset--color--bronze-deep);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Skip link — hidden until focused. */
.jie-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 999;
  padding: .75rem 1.25rem;
  background: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--cream);
  font-size: var(--wp--preset--font-size--small);
  font-weight: 600;
  text-decoration: none;
}
.jie-skip-link:focus { left: 1rem; top: 1rem; }

/* Images should never overflow or distort. */
img, video { max-width: 100%; height: auto; }

/* WordPress applies the global block gap between the top-level blocks in a
   template, which put 24px of page background between the header and the
   hero. Each template supplies its own top spacing (a padded title group, or
   a section with its own padding), so main does not need the margin — and the
   hero is meant to butt straight up against the header rule. */
.wp-site-blocks > main { margin-block-start: 0; }

/* ---------------------------------------------------------------- header -- */

/* Fixed, not sticky.
   The header shrinks on scroll, and a sticky element that changes height
   drags the whole document up with it — the page visibly jumps at the
   threshold. Fixed takes it out of flow; .wp-site-blocks carries a spacer
   equal to its FULL height, so shrinking moves nothing. */
.jie-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 300;
  background: var(--wp--preset--color--white);
  border-bottom: var(--wp--custom--rule);
  transition: background-color 220ms ease, box-shadow 220ms ease,
              backdrop-filter 220ms ease;
}

.wp-site-blocks { padding-top: var(--jie-header-full, 88px); }

/* Landscape phones: a fixed header would eat a quarter of the viewport and
   never give it back. */
@media (max-height: 480px) {
  .jie-header { position: static; }
  .wp-site-blocks { padding-top: 0; }
}

/* Anchor targets need to clear the fixed header, or #process lands with its
   heading hidden underneath it. */
:target,
[id] { scroll-margin-top: calc(var(--jie-header-full, 88px) + 1rem); }

.jie-header__inner {
  max-width: 1360px;
  margin-inline: auto;
  padding-inline: var(--wp--style--root--padding-right, 1.5rem);
  padding-block: 0.9rem;
  gap: clamp(1rem, 3vw, 2.5rem);
  transition: padding-block 220ms ease;
}

.jie-header__logo img {
  display: block;
  width: auto;
  height: clamp(42px, 4.4vw, 58px);
  transition: height 220ms ease;
}

/* --- scrolled state ------------------------------------------------------ */

.jie-header.is-scrolled {
  /* 0.92 rather than something more obviously see-through. The nav sits over
     whatever happens to be scrolling underneath, and at this alpha even pure
     black content leaves the labels at 12:1. Anything lighter starts putting
     the smaller text near the AA line for the sake of an effect nobody asked
     for. */
  background: rgba(255, 255, 255, 0.92);
  border-bottom-color: transparent;
  box-shadow: 0 1px 0 rgba(19, 20, 21, .07), 0 8px 28px rgba(19, 20, 21, .07);
}

/* Progressive: without backdrop-filter the rgba background alone is fine. */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .jie-header.is-scrolled {
    background: rgba(255, 255, 255, 0.86);
    -webkit-backdrop-filter: saturate(150%) blur(12px);
    backdrop-filter: saturate(150%) blur(12px);
  }
}

.jie-header.is-scrolled .jie-header__inner { padding-block: 0.5rem; }
.jie-header.is-scrolled .jie-header__logo img { height: clamp(34px, 3.2vw, 44px); }
.jie-header.is-scrolled .jie-btn--sm { padding: 0.6rem 1.2rem; }

/* Uppercase, letterspaced navigation, as in the client's mock.
   Scoped to the navigation block: `.jie-nav a` also matched the "Get a quote"
   button and the phone number, which live in the same flex container, and
   overrode the button's white label with charcoal — a real contrast failure
   that axe caught. */
.jie-nav .wp-block-navigation a {
  text-decoration: none;
  color: var(--wp--preset--color--charcoal);
  font-size: var(--wp--preset--font-size--x-small);
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  padding-block: .35rem;
  border-bottom: 2px solid transparent;
  transition: border-color var(--wp--custom--transition), color var(--wp--custom--transition);
}
.jie-nav .wp-block-navigation a:hover,
.jie-nav .current-menu-item > a {
  color: var(--wp--preset--color--ink);
  border-bottom-color: var(--wp--preset--color--bronze);
}

/* Target .jie-nav__wrap, the class this theme puts on the navigation block's
   own <ul>. An earlier .jie-nav__list selector matched nothing at all — the
   block renders .wp-block-navigation__container — so the mobile menu quietly
   kept WordPress's default horizontal flex and the links wrapped across two
   lines instead of stacking. */
.jie-nav__wrap {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(1rem, 2.2vw, 1.9rem);
}

.jie-header__actions {
  display: flex;
  align-items: center;
  gap: clamp(0.9rem, 1.8vw, 1.5rem);
}

/* The header button is smaller than the hero's. */
.jie-btn--sm { padding: 0.75rem 1.35rem; transition: padding 220ms ease; }

/* The phone number is a real conversion path, not a footnote — but in the
   header the button leads, so it is set at label size rather than display. */
.jie-phone {
  font-size: var(--wp--preset--font-size--small);
  font-weight: 700;
  color: var(--wp--preset--color--ink);
  text-decoration: none;
  white-space: nowrap;
  letter-spacing: .01em;
}
/* Underline rather than a colour change. The scrolled header is translucent,
   and bronze-deep over worst-case dark content measures 4.24:1 — just under
   AA. Ink stays at 12:1 whatever is behind it. */
.jie-phone:hover,
.jie-phone:focus-visible { text-decoration: underline; text-underline-offset: 3px; }

/* Below the breakpoint the header would be logo + nav + phone + button, which
   does not fit. The nav panel takes over and carries the phone with it. */
@media (max-width: 1099px) {
  .jie-header__actions .jie-phone { display: none; }
}

/* ------------------------------------------------------------------ hero -- */

/* The photograph runs the full width of the hero and a cream gradient fades
   across it from the left, with the copy sitting in the solid part. This is
   how the client's mock is built, and it is better than a hard two-column
   split: the kitchen gets to be a wide photograph instead of a cropped panel,
   and the join between text and image is soft rather than a seam.
   It is the inverse of the usual dark-scrim hero — cream over the image,
   dark text on top — which suits a bright interior photograph far better. */

.jie-hero {
  position: relative;
  background: var(--wp--preset--color--cream);
  overflow: hidden;
  min-height: clamp(420px, 60vh, 620px);
  display: grid;
  align-items: center;
}

.jie-hero__media { position: absolute; inset: 0; z-index: 0; }

.jie-hero__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 38%;
}

/* The fade. Solid cream under the copy, gone by the time it reaches the
   middle of the frame.
   Horizontal only — an earlier version also washed cream over the top edge to
   soften the join with the header, but that read as a gap once the photograph
   was flush against it. The top edge is meant to be a clean butt join. */
.jie-hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(96deg,
    rgba(245,242,236,1)    0%,
    rgba(245,242,236,1)   34%,
    rgba(245,242,236,.92) 42%,
    rgba(245,242,236,.55) 52%,
    rgba(245,242,236,.12) 64%,
    rgba(245,242,236,0)   74%);
}

/* Below the breakpoint a side fade cannot work — there is no width to fade
   across. The image becomes a band beneath the copy instead. */
@media (max-width: 899px) {
  .jie-hero {
    display: block;
    min-height: 0;
    overflow: visible;
  }
  .jie-hero__media {
    position: relative;
    inset: auto;
    aspect-ratio: 4 / 3;
  }
  .jie-hero__media::after { display: none; }
}

.jie-hero__grid {
  position: relative;
  z-index: 1;
  width: 100%;
}

@media (max-width: 899px) {
  .jie-hero__grid { display: flex; flex-direction: column-reverse; }
}

.jie-hero__panel {
  width: 100%;
  max-width: 1240px;
  margin-inline: auto;
  padding-block: clamp(2.5rem, 6vw, 4rem);
  padding-inline: var(--wp--style--root--padding-right, 1.5rem);
}

.jie-hero__copy { max-width: 30rem; }

.jie-hero__title {
  font-size: clamp(2.25rem, 4vw, 3.5rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.025em;
  color: var(--wp--preset--color--ink);
  margin: 0;
}

/* Display-size text, so the brand bronze is cleared for use here.
   Anything smaller must use bronze-deep — see brand/palette.md. */
.jie-hero__accent { color: var(--wp--preset--color--bronze); }

.jie-hero__rule {
  display: block;
  width: 56px;
  height: 2px;
  background: var(--wp--preset--color--bronze);
  margin: clamp(1.1rem, 2.2vw, 1.6rem) 0;
}

.jie-hero__lede {
  margin: 0;
  font-size: var(--wp--preset--font-size--small);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  line-height: 1.75;
  color: var(--wp--preset--color--charcoal);
}

.jie-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  margin: clamp(1.5rem, 2.8vw, 2rem) 0 0;
}

/* ----------------------------------------------------------------- buttons -- */

/* Used by the hero and anywhere else that needs a button outside the block
   editor's own button block. Fill is bronze-deep, not bronze: the label is
   small text, and white on bronze is only 4.3:1. */
.jie-btn {
  display: inline-block;
  background: var(--wp--preset--color--bronze-deep);
  color: var(--wp--preset--color--white);
  font-size: var(--wp--preset--font-size--x-small);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 1rem 1.9rem;
  border: 1px solid var(--wp--preset--color--bronze-deep);
  border-radius: 2px;
  transition: background var(--wp--custom--transition), color var(--wp--custom--transition),
              border-color var(--wp--custom--transition);
}
.jie-btn:hover,
.jie-btn:focus-visible {
  background: var(--wp--preset--color--ink);
  border-color: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--white);
}

.jie-btn--ghost {
  background: transparent;
  color: var(--wp--preset--color--charcoal);
  border-color: var(--wp--preset--color--taupe-light);
}
.jie-btn--ghost:hover,
.jie-btn--ghost:focus-visible {
  background: var(--wp--preset--color--ink);
  border-color: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--white);
}

.jie-eyebrow {
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--x-small);
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--bronze-lift);
  margin-bottom: .75rem;
}
.jie-eyebrow--dark { color: var(--wp--preset--color--bronze-deep); }

/* ------------------------------------------------------------- trust bar -- */

.jie-trust {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--wp--preset--spacing--30);
  border-block: var(--wp--custom--rule);
  padding-block: var(--wp--preset--spacing--40);
}
.jie-trust__item { text-align: center; }
.jie-trust__figure {
  display: block;
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--x-large);
  font-weight: 600;
  color: var(--wp--preset--color--ink);
  line-height: 1.1;
}
.jie-trust__label {
  display: block;
  margin-top: .4rem;
  font-size: var(--wp--preset--font-size--x-small);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--wp--preset--color--taupe);
}

/* -------------------------------------------------------------- services -- */

.jie-services {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
  gap: var(--wp--preset--spacing--30);
}

.jie-service {
  background: var(--wp--preset--color--white);
  border: var(--wp--custom--rule);
  padding: var(--wp--preset--spacing--30);
  display: flex;
  flex-direction: column;
  gap: .6rem;
  transition: box-shadow var(--wp--custom--transition), transform var(--wp--custom--transition);
}
.jie-service:hover { box-shadow: var(--wp--preset--shadow--soft); transform: translateY(-2px); }
.jie-service h3 { margin: 0; }
.jie-service p { margin: 0; color: var(--wp--preset--color--taupe); font-size: var(--wp--preset--font-size--small); }
.jie-service__rule { width: 34px; height: 2px; background: var(--wp--preset--color--bronze); }

/* --------------------------------------------------------------- projects -- */

/* Masonry-ish columns. The library is 60% portrait, so a fixed-ratio grid
   would either crop the best shots or leave large gaps. CSS columns let each
   tile keep its own aspect ratio. */
.jie-projects {
  columns: 3;
  column-gap: var(--wp--preset--spacing--30);
}
@media (max-width: 1024px) { .jie-projects { columns: 2; } }
@media (max-width: 600px)  { .jie-projects { columns: 1; } }

.jie-project {
  break-inside: avoid;
  margin-bottom: var(--wp--preset--spacing--30);
  display: block;
  text-decoration: none;
  position: relative;
  background: var(--wp--preset--color--ink);
}
.jie-project img {
  display: block;
  width: 100%;
  transition: opacity var(--wp--custom--transition), transform 400ms cubic-bezier(.4,0,.2,1);
}
.jie-project:hover img { opacity: .82; transform: scale(1.015); }

.jie-project__body {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--20) var(--wp--preset--spacing--20);
  background: linear-gradient(to top, rgba(19,20,21,.88), rgba(19,20,21,0));
}
.jie-project__title {
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--large);
  font-weight: 600;
  color: var(--wp--preset--color--cream);
  line-height: 1.2;
  margin: 0;
}
.jie-project__meta {
  display: block;
  margin-top: .3rem;
  font-size: var(--wp--preset--font-size--x-small);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--wp--preset--color--bronze-lift);
}

/* Category filter */
.jie-filter { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: var(--wp--preset--spacing--40); }
.jie-filter a {
  text-decoration: none;
  font-size: var(--wp--preset--font-size--x-small);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--taupe);
  border: 1px solid var(--wp--preset--color--taupe-light);
  padding: .5rem 1rem;
  transition: all var(--wp--custom--transition);
}
.jie-filter a:hover,
.jie-filter a[aria-current="page"] {
  color: var(--wp--preset--color--white);
  background: var(--wp--preset--color--ink);
  border-color: var(--wp--preset--color--ink);
}

/* --------------------------------------------------------- project detail -- */

/* .jie-shell is width only — no layout opinion.
   Width and layout were briefly the same class, and the moment the project
   header became a two-column grid every other section that borrowed it for
   its width became a grid too: the "More of our work" eyebrow and heading
   silently split into separate columns. Keeping them separate means a section
   can take the container width without inheriting somebody else's layout.
   Flow inside, so children share one left edge without relying on the
   auto-margins a constrained layout would apply. */
.jie-shell {
  max-width: 1240px;
  margin-inline: auto;
  padding-inline: var(--wp--style--root--padding-right, 1.5rem);
}

.jie-project-head .wp-block-post-title { margin-block: .35rem 0; }

/* Prose gets a reading measure; galleries keep the full container width. */
.jie-project-body .entry-content > :is(p, ul, ol, h2, h3, h4) { max-width: 46rem; }

.jie-project-lede {
  max-width: 46rem;
  font-size: var(--wp--preset--font-size--large);
  line-height: 1.55;
  color: var(--wp--preset--color--taupe);
  margin-block-start: 1rem;
}

/* The header: copy and photograph side by side.
   The featured image originally rendered at its natural proportions across the
   full viewport — a 1200x1600 portrait became 1440x1920, a two-screen-tall
   wall of photograph before a single word. Cropping it to a wide banner fixed
   the height but reduced portrait shots to an unreadable 3.5:1 sliver.
   A roughly square column is the orientation-neutral answer: portrait and
   landscape each lose about the same amount, so every project reads alike. */
@media (min-width: 900px) {
  .jie-project-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
    gap: clamp(2rem, 5vw, 4rem);
    align-items: center;
  }
}

.jie-project-head__text { min-width: 0; }

.jie-project-head__media { margin-block: 0; min-width: 0; }
.jie-project-head__media img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  object-position: center 42%;
}

@media (max-width: 899px) {
  .jie-project-head__media { margin-block-start: var(--wp--preset--spacing--40); }
  .jie-project-head__media img { aspect-ratio: 4 / 3; }
}

.jie-project-body h2 {
  font-size: var(--wp--preset--font-size--large);
  margin-block: var(--wp--preset--spacing--40) var(--wp--preset--spacing--20);
}
.jie-project-body h2:first-child { margin-block-start: 0; }

/* Grid, not multi-column.
   Multicol was tried first, to let each photograph keep its own proportions.
   It balances by height and will not be told otherwise: a four-image gallery
   packed 2+2 into two columns and left the third empty. Filling all three
   would need the pixel heights, which CSS cannot know before layout, and
   `grid-template-rows: masonry` is not shipping widely yet.
   A grid always fills the width. Images keep their natural proportions and
   top-align, so a short one leaves whitespace beneath rather than being
   stretched or cropped — these are the work itself, not thumbnails. */
.jie-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr));
  gap: var(--wp--preset--spacing--20);
  align-items: start;
}
.jie-gallery figure { margin: 0; }
.jie-gallery img { display: block; width: 100%; }

/* Before/after pairs sit side by side and are labelled, so the sequence reads
   correctly even when the images are viewed out of context. */
.jie-comparison {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: var(--wp--preset--spacing--20);
}
.jie-comparison figure { margin: 0; position: relative; }
.jie-comparison img { display: block; width: 100%; }
.jie-comparison figcaption {
  position: absolute;
  top: 0; left: 0;
  background: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--cream);
  font-size: var(--wp--preset--font-size--x-small);
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  padding: .4rem .85rem;
}

.jie-scope { list-style: none; padding: 0; margin: 0; display: grid; gap: .55rem; }
.jie-scope li { position: relative; padding-left: 1.5rem; color: var(--wp--preset--color--charcoal); }
.jie-scope li::before {
  content: "";
  position: absolute;
  left: 0; top: .62em;
  width: 8px; height: 2px;
  background: var(--wp--preset--color--bronze);
}

/* --------------------------------------------------------------- lightbox -- */

.jie-lightbox-link {
  display: block;
  position: relative;
  cursor: zoom-in;
}
.jie-lightbox-link img { transition: opacity var(--wp--custom--transition); }
.jie-lightbox-link:hover img { opacity: .88; }

.jie-lb {
  border: 0;
  padding: 0;
  max-width: 100vw;
  max-height: 100dvh;
  width: 100%;
  height: 100%;
  /* An opaque background rather than a transparent dialog over a ::backdrop.
     With a transparent dialog the caption's effective background is
     indeterminate, so a contrast checker resolves it against the cream page
     behind and reports a failure it cannot verify either way. A real colour
     makes the contrast computable — and actually guaranteed. */
  background: #0F1011;
  overflow: hidden;
}
.jie-lb::backdrop { background: #0F1011; }

.jie-lb__inner {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(.5rem, 2vw, 1.5rem);
  width: 100%;
  height: 100%;
  padding: clamp(3rem, 6vw, 4rem) clamp(.75rem, 3vw, 2.5rem);
}

.jie-lb__figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  min-height: 0;
}

.jie-lb__img {
  display: block;
  max-width: 100%;
  /* Leave room for the caption; 100% would push it off the bottom on short
     viewports with a portrait photograph. */
  max-height: calc(100dvh - 11rem);
  width: auto;
  height: auto;
  object-fit: contain;
  background: #0a0a0b;
}

.jie-lb.is-loading .jie-lb__img { opacity: .35; }

.jie-lb__caption {
  text-align: center;
  color: #E6E2DA;
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.5;
  max-width: 60ch;
}
.jie-lb__count {
  display: block;
  margin-top: .35rem;
  font-size: var(--wp--preset--font-size--x-small);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--wp--preset--color--bronze-lift);
}

.jie-lb__close,
.jie-lb__nav {
  appearance: none;
  border: 1px solid #4A4844;
  background: #1B1C1D;
  color: var(--wp--preset--color--cream);
  border-radius: 999px;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background var(--wp--custom--transition), border-color var(--wp--custom--transition);
}
.jie-lb__close:hover,
.jie-lb__nav:hover {
  background: var(--wp--preset--color--bronze-deep);
  border-color: var(--wp--preset--color--bronze-deep);
}

.jie-lb__nav {
  width: clamp(2.5rem, 5vw, 3.25rem);
  height: clamp(2.5rem, 5vw, 3.25rem);
  font-size: 1.6rem;
  line-height: 1;
}
.jie-lb__nav[hidden] { display: none; }

.jie-lb__close {
  position: absolute;
  top: clamp(.75rem, 2vw, 1.25rem);
  right: clamp(.75rem, 2vw, 1.25rem);
  width: 2.75rem;
  height: 2.75rem;
  font-size: 1.5rem;
  line-height: 1;
  z-index: 2;
}

/* On narrow screens the side arrows would eat the photograph, so they move
   under it. */
@media (max-width: 560px) {
  .jie-lb__inner {
    grid-template-columns: 1fr;
    grid-template-areas: "figure" "nav";
    align-content: center;
  }
  .jie-lb__figure { grid-area: figure; }
  .jie-lb__nav {
    grid-area: nav;
    justify-self: center;
  }
  .jie-lb__nav--prev { justify-self: start; }
  .jie-lb__nav--next { justify-self: end; }
  .jie-lb__img { max-height: calc(100dvh - 15rem); }
}

/* ------------------------------------------------------ related projects -- */

.jie-related {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: var(--wp--preset--spacing--30);
}

.jie-related__item { text-decoration: none; display: block; }

/* A fixed crop here, unlike the masonry archive grid: a row of three wants to
   be even, and these are thumbnails rather than the work itself. */
.jie-related__frame {
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--wp--preset--color--ink);
}
.jie-related__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 400ms cubic-bezier(.4,0,.2,1), opacity var(--wp--custom--transition);
}
.jie-related__item:hover .jie-related__frame img { transform: scale(1.03); opacity: .9; }

.jie-related__meta {
  display: block;
  margin-top: .8rem;
  font-size: var(--wp--preset--font-size--x-small);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--wp--preset--color--bronze-deep);
}
.jie-related__title {
  display: block;
  margin-top: .25rem;
  font-size: var(--wp--preset--font-size--large);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.015em;
  color: var(--wp--preset--color--ink);
}
.jie-related__item:hover .jie-related__title { color: var(--wp--preset--color--bronze-deep); }

/* ----------------------------------------------------------- testimonials -- */

.jie-testimonials {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  gap: var(--wp--preset--spacing--30);
}

.jie-testimonial {
  background: var(--wp--preset--color--white);
  border: var(--wp--custom--rule);
  padding: var(--wp--preset--spacing--40) var(--wp--preset--spacing--30);
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--20);
}
.jie-testimonial blockquote {
  margin: 0;
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--large);
  line-height: 1.45;
  color: var(--wp--preset--color--ink);
}
.jie-testimonial blockquote p { margin: 0 0 .8em; }
.jie-testimonial blockquote p:last-child { margin-bottom: 0; }
.jie-testimonial footer {
  margin-top: auto;
  font-size: var(--wp--preset--font-size--small);
  color: var(--wp--preset--color--taupe);
}
.jie-testimonial cite { font-style: normal; font-weight: 600; color: var(--wp--preset--color--charcoal); }

/* Stars are decorative — the rating is announced via the adjacent
   visually-hidden text, so the glyphs themselves are hidden from AT. */
.jie-stars { color: var(--wp--preset--color--bronze); letter-spacing: .18em; font-size: 1.05rem; }

.jie-visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------- process -- */

.jie-process { counter-reset: step; display: grid; gap: var(--wp--preset--spacing--30); }
@media (min-width: 780px) { .jie-process { grid-template-columns: repeat(4, 1fr); } }

.jie-process__step { counter-increment: step; position: relative; padding-top: var(--wp--preset--spacing--30); }
.jie-process__step::before {
  content: counter(step, decimal-leading-zero);
  position: absolute;
  top: 0; left: 0;
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--large);
  font-weight: 600;
  color: var(--wp--preset--color--bronze);
}
.jie-process__step::after {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--wp--preset--color--taupe-light);
  opacity: .45;
}
.jie-process__step h3 { margin: 0 0 .4rem; font-size: var(--wp--preset--font-size--medium); }
.jie-process__step p  { margin: 0; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--taupe); }

/* ---------------------------------------------------------------- footer -- */

.jie-footer {
  background: var(--wp--preset--color--ink);
  color: rgba(245,242,236,.78);
}
.jie-footer a { color: var(--wp--preset--color--bronze-lift); text-decoration: none; }
.jie-footer a:hover { color: var(--wp--preset--color--cream); text-decoration: underline; }
.jie-footer h2, .jie-footer h3 { color: var(--wp--preset--color--cream); }
.jie-footer__logo img { height: 48px; width: auto; display: block; }
.jie-footer__legal {
  border-top: 1px solid rgba(245,242,236,.14);
  padding-top: var(--wp--preset--spacing--30);
  font-size: var(--wp--preset--font-size--x-small);
  color: rgba(245,242,236,.62);
}

/* PA law requires the Home Improvement Contractor number on advertising.
   It is small, but it must be legible — not a 10px grey whisper. */
.jie-license { letter-spacing: .04em; }

/* ------------------------------------------------------------------ forms -- */

.jie-form { display: grid; gap: var(--wp--preset--spacing--20); }
.jie-form__row { display: grid; gap: var(--wp--preset--spacing--20); }
@media (min-width: 620px) { .jie-form__row--2 { grid-template-columns: 1fr 1fr; } }

.jie-form label {
  display: block;
  font-size: var(--wp--preset--font-size--x-small);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--taupe);
  margin-bottom: .4rem;
}
.jie-form .req { color: var(--wp--preset--color--bronze-deep); }

.jie-form input,
.jie-form select,
.jie-form textarea {
  /* The [class^="jie-"] reset above matches components, not the bare form
     controls inside them, so these still need it explicitly. */
  box-sizing: border-box;
  width: 100%;
  font-family: inherit;
  font-size: var(--wp--preset--font-size--medium);
  color: var(--wp--preset--color--charcoal);
  background: var(--wp--preset--color--white);
  border: 1px solid var(--wp--preset--color--taupe-light);
  border-radius: 2px;
  padding: .8rem .9rem;
  transition: border-color var(--wp--custom--transition), box-shadow var(--wp--custom--transition);
}
.jie-form input:focus,
.jie-form select:focus,
.jie-form textarea:focus {
  border-color: var(--wp--preset--color--bronze-deep);
  box-shadow: 0 0 0 3px rgba(138,86,32,.14);
  outline: none;
}
.jie-form textarea { min-height: 9rem; resize: vertical; }

.jie-form__note { font-size: var(--wp--preset--font-size--x-small); color: var(--wp--preset--color--taupe); }

.jie-notice { padding: var(--wp--preset--spacing--30); border-left: 3px solid; }
.jie-notice--ok    { border-color: #2E7D4F; background: #EEF6F1; }
.jie-notice--error { border-color: #B3261E; background: #FBEEEE; }

/* Honeypot. Off-screen rather than display:none — some bots skip hidden
   fields, but almost all of them fill a field they can "see" in the DOM. */
.jie-hp {
  position: absolute !important;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

/* ------------------------------------------------------------ placeholder -- */

/* Deliberately loud. A placeholder that looks like finished copy is worse than
   no page at all — someone will read it, believe it, and quote it back. */
.jie-placeholder {
  border: 2px dashed var(--wp--preset--color--bronze-deep);
  background: #FBF3E7;
  padding: var(--wp--preset--spacing--30);
  margin-bottom: var(--wp--preset--spacing--40);
  max-width: 46rem;
}
.jie-placeholder__flag {
  margin: 0 0 .5rem;
  font-size: var(--wp--preset--font-size--x-small);
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--wp--preset--color--bronze-deep);
}
.jie-placeholder__body {
  margin: 0;
  color: var(--wp--preset--color--charcoal);
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.6;
}

.jie-todo {
  max-width: 46rem;
  margin: var(--wp--preset--spacing--20) 0 var(--wp--preset--spacing--40);
  padding-left: 1.4rem;
  display: grid;
  gap: var(--wp--preset--spacing--20);
}
.jie-todo li { padding-left: .35rem; line-height: 1.6; }
.jie-todo li::marker { color: var(--wp--preset--color--bronze-deep); font-weight: 700; }

/* ----------------------------------------------------------- utilities --- */

.jie-rule-top { border-top: var(--wp--custom--rule); }

/* .jie-measure has been removed. It capped a paragraph's line length, but in
   a constrained container that made it narrower than its own heading, so it
   either sat centred (looking indented) or, with the margins forced to zero,
   escaped the container entirely and aligned to the viewport edge — which is
   how a project page ended up with its title centred and its excerpt hard
   against the left margin.
   Line length is now set by the container's contentSize, so every child of a
   section shares one measure and one left edge. Where a section needs a
   narrower column than the default 760px, give the group an explicit
   contentSize rather than styling the paragraph. */

.jie-panel-dark {
  background: var(--wp--preset--color--ink);
  color: rgba(245,242,236,.86);
}
.jie-panel-dark h2, .jie-panel-dark h3 { color: var(--wp--preset--color--cream); }
.jie-panel-dark .wp-element-button {
  background: var(--wp--preset--color--bronze);
  color: var(--wp--preset--color--ink);
}
.jie-panel-dark .wp-element-button:hover {
  background: var(--wp--preset--color--cream);
  color: var(--wp--preset--color--ink);
}

/* ---------------------------------------------------------- mobile nav --- */

/* The open panel used to cover the whole viewport at z-index 200, above the
   header at 100 — which buried the only control that could close it. There
   was no close button inside the panel either, so on a touch device the only
   way out was to tap a navigation link. Escape worked, which is no help on a
   phone.
   The header now sits ABOVE the panel and the panel starts below it, so the
   logo stays put and the toggle stays reachable and becomes the close button. */

.jie-nav-toggle { display: none; }
.jie-nav-toggle__close { display: none; }

@media (max-width: 899px) {
  .jie-has-js .jie-nav-toggle {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    background: none;
    border: 1px solid var(--wp--preset--color--taupe-light);
    border-radius: 2px;
    padding: .6rem .9rem;
    font: inherit;
    font-size: var(--wp--preset--font-size--x-small);
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--wp--preset--color--charcoal);
    cursor: pointer;
    /* Comfortable thumb target. */
    min-height: 44px;
    min-width: 44px;
    justify-content: center;
  }

  .jie-has-js .jie-nav-toggle[aria-expanded="true"] {
    background: var(--wp--preset--color--ink);
    border-color: var(--wp--preset--color--ink);
    color: var(--wp--preset--color--cream);
  }
  .jie-has-js .jie-nav-toggle[aria-expanded="true"] .jie-nav-toggle__open { display: none; }
  .jie-has-js .jie-nav-toggle[aria-expanded="true"] .jie-nav-toggle__close { display: inline; }

  .jie-has-js [data-jie-nav-panel] {
    position: fixed;
    /* Starts below the header rather than covering it. --jie-header-h is
       measured and set by nav.js, with a sane fallback. */
    top: var(--jie-header-h, 72px);
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 200;
    background: var(--wp--preset--color--cream);
    padding: var(--wp--preset--spacing--40) var(--wp--preset--spacing--30) var(--wp--preset--spacing--50);
    overflow-y: auto;
    overscroll-behavior: contain;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--wp--preset--spacing--40);
  }
  .jie-has-js [data-jie-nav-panel][hidden] { display: none; }

  .jie-has-js [data-jie-nav-panel] .jie-nav__wrap {
    flex-direction: column;
    align-items: stretch;
    flex-wrap: nowrap;
    gap: 0;
    width: 100%;
  }
  .jie-has-js [data-jie-nav-panel] .jie-nav__wrap > li {
    border-bottom: var(--wp--custom--rule);
    width: 100%;
  }
  .jie-has-js [data-jie-nav-panel] .wp-block-navigation a {
    display: block;
    font-size: var(--wp--preset--font-size--large);
    letter-spacing: .04em;
    padding-block: .95rem;
    border-bottom: 0;
  }

  /* Tap-to-call belongs in the mobile menu, where it matters most. */
  .jie-has-js [data-jie-nav-panel] .jie-header__actions {
    flex-direction: column;
    align-items: stretch;
    gap: var(--wp--preset--spacing--20);
    margin-top: auto;
  }
  .jie-has-js [data-jie-nav-panel] .jie-header__actions .jie-phone {
    display: block;
    font-size: var(--wp--preset--font-size--x-large);
    text-align: center;
    padding-block: .5rem;
  }
  .jie-has-js [data-jie-nav-panel] .jie-btn { text-align: center; }
}

@media (min-width: 900px) {
  [data-jie-nav-panel][hidden] { display: flex !important; }
}

/* Used for a single frame while nav.js measures the unscrolled header height;
   suppresses the transition so the measurement cannot animate. */
.jie-header.jie-measuring { transition: none !important; }
.jie-header.jie-measuring * { transition: none !important; }
