/* ==========================================================================
   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);
  /* backdrop-filter is deliberately NOT transitioned.
     Animating it meant the property interpolated blur(12px) -> none over
     220ms, and for every one of those frames the computed value was still a
     filter — so the header stayed the containing block for the fixed nav
     panel and the menu rendered at 88px before snapping to full height. A
     visible jump on open, caused entirely by easing a property nobody notices
     easing. The background-colour transition carries the effect on its own. */
  transition: background-color 220ms ease, box-shadow 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;
}

/* The hero photograph is a core/image block now, so this is a <figure> rather
   than a plain div — hence the margin reset. Being a real block is the point:
   it can be swapped from wp-admin instead of being set in PHP. */
.jie-hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  margin: 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; }
}

/* The stacking context lives here.
   It used to be on a .jie-hero__grid wrapper, which the block conversion
   removed — and without it the absolutely-positioned photograph (z-index 0)
   painted over the static copy, so the headline rendered in the HTML and was
   invisible on screen. A positioned element with z-index 0 still sits above
   in-flow content that has no position of its own. */
.jie-hero__panel {
  position: relative;
  z-index: 1;
  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__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;
}

/* Bronze accent words. <em> rather than a custom span so the client can mark
   words up from the editor toolbar — italic is the closest thing the block
   toolbar offers to "make this word bronze", and the theme reinterprets it.
   Display-size text, so the brand bronze is cleared for use here; anything
   smaller must use bronze-deep. See brand/palette.md. */
.jie-hero__title em {
  font-style: normal;
  color: var(--wp--preset--color--bronze);
}

/* The short rule under the headline. A pseudo-element rather than a separator
   block, so there is one less thing for someone to delete by accident. */
.jie-hero__lede::before {
  content: "";
  display: block;
  width: 56px;
  height: 2px;
  background: var(--wp--preset--color--bronze);
  margin-bottom: clamp(1.1rem, 2.2vw, 1.6rem);
}

.jie-hero__lede {
  margin: 0;
  max-width: 34rem;
  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);
}
/* Figure and label are paragraphs now, not spans — they are editable blocks,
   and a block has to be something the editor can put a cursor in. */
.jie-trust__item { text-align: center; }
.jie-trust__figure {
  margin: 0;
  font-size: var(--wp--preset--font-size--x-large);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--wp--preset--color--ink);
  line-height: 1.1;
}
.jie-trust__label {
  margin: .4rem 0 0;
  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); }

/* Was a decorative <span> in the markup; a pseudo-element keeps it out of the
   editor entirely, where an empty span reads as a stray block. */
.jie-service::before {
  content: "";
  display: block;
  width: 34px;
  height: 2px;
  background: var(--wp--preset--color--bronze);
}

.jie-service h3 { margin: 0; font-size: var(--wp--preset--font-size--large); }
.jie-service p { margin: 0; color: var(--wp--preset--color--taupe); font-size: var(--wp--preset--font-size--small); }

/* --------------------------------------------------------------- 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. */
/* The project galleries are core/gallery blocks carrying .jie-gallery, so that
   the client edits them natively while they still lay out as a grid.

   Core styles the gallery as flex via `.wp-block-gallery.has-nested-images`,
   which is two class selectors — so matching that specificity is the point of
   the doubled selector here, not an accident. A single .jie-gallery loses. */
.jie-gallery,
.jie-project-body .wp-block-gallery.has-nested-images,
.editor-styles-wrapper .wp-block-gallery.has-nested-images {
  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; }

/* The editor selector is here on purpose: theme.css is loaded as an editor
   stylesheet, and without it core's flex layout makes a lone tile on the last
   row grow to the full width — four photographs showed as 237, 237, 237 and
   then one at 760. The client should see the grid they are going to get.

   The :not(#individual-image) is not defensive coding, it is arithmetic. Core
   sizes gallery children with
     .wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image)
   and that :not() carries an ID, so the selector scores (1,3,0). A sane
   (0,4,0) selector loses to it, which is why the tiles first rendered 180px
   wide inside 588px grid columns. Matching the trick and adding one class
   beats it honestly, without !important. */
.jie-project-body .wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image),
.editor-styles-wrapper .wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) {
  margin: 0;
  width: auto;
  max-width: none;
  flex-grow: 0;
}
.jie-gallery img,
.jie-project-body .wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) img,
.editor-styles-wrapper .wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) img {
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
}
/* Core absolutely positions gallery captions over the image. These galleries
   use the alt text for the lightbox caption instead, and a caption bar over
   every tile would fight the grid. */
.jie-project-body .wp-block-gallery.has-nested-images figure.wp-block-image figcaption {
  position: static;
  background: none;
  padding: 0.4rem 0 0;
  color: var(--wp--preset--color--taupe);
  font-size: var(--wp--preset--font-size--x-small);
}

/* 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; }

/* Town and year, under the project excerpt. Absent entirely when neither has
   been filled in, so this rule matches nothing on most projects today. */
.jie-project-facts {
  margin: var(--wp--preset--spacing--20) 0 0;
  font-size: var(--wp--preset--font-size--small);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--wp--preset--color--taupe);
}

/* ------------------------------------------------------------- about --- */

/* Full-bleed band under the About intro. Fixed aspect rather than natural
   height: the crew photographs vary from 4:3 to 3:2 and a natural-height
   banner jumps around as they are swapped. */
.jie-about-banner {
  margin-block: var(--wp--preset--spacing--40) 0;
}
.jie-about-banner .jie-about-banner__media,
.jie-about-banner figure {
  margin: 0;
}
.jie-about-banner img {
  display: block;
  width: 100%;
  height: clamp(240px, 38vw, 460px);
  object-fit: cover;
  /* The group shot has people across the lower half; centre-cropping a wide
     band cuts their heads off. */
  object-position: center 42%;
}

/* Lead photograph for the team section. A fixed band rather than the natural
   4:3 height, which at the wide width would run 620px tall and push the crew
   grid below the fold. object-position sits above centre because the three
   faces are in the upper half of the frame and a centred crop clips them. */
.jie-crew-lead {
  margin-block: var(--wp--preset--spacing--30) 0;
}
.jie-crew-lead img {
  display: block;
  width: 100%;
  aspect-ratio: 2 / 1;
  object-fit: cover;
  object-position: center 34%;
}

/* Crew photographs. Grid, not columns: multi-column balanced four portrait
   images two-and-two and left the last column empty, which is the same trap
   the project gallery hit. */
.jie-crew {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--wp--preset--spacing--20);
  margin-block-start: var(--wp--preset--spacing--40);
}
.jie-crew figure,
.jie-crew .jie-crew__shot {
  margin: 0;
}
.jie-crew img {
  display: block;
  width: 100%;
  /* One ratio for every tile so the rows line up, with object-fit doing the
     cropping rather than the image being squashed to fit. */
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

@media (max-width: 900px) {
  .jie-crew { grid-template-columns: repeat(2, 1fr); }
  /* Nine tiles over two columns strands the last one on a row of its own. Let
     it run the full width instead, on a wider crop so it does not tower over
     the tiles above it. */
  .jie-crew > :last-child { grid-column: 1 / -1; }
  .jie-crew > :last-child img { aspect-ratio: 2 / 1; }
}

@media (max-width: 540px) {
  .jie-crew { grid-template-columns: 1fr; }
  /* One column: nothing is stranded, so the last tile goes back to 4:3. */
  .jie-crew > :last-child img { aspect-ratio: 4 / 3; }
  .jie-about-banner img { height: clamp(200px, 52vw, 320px); }
  /* At phone width a 2:1 band leaves the three faces too small to make out. */
  .jie-crew-lead img { aspect-ratio: 4 / 3; }
}

/* ----------------------------------------------------------- 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 --- */

/* Neutralise the header's containing-block properties while the menu is open.
 *
 * The nav panel lives inside <header> — it has to, because on desktop it is a
 * flex item in the header row — and it becomes `position: fixed` on mobile.
 * `backdrop-filter` on an ancestor makes that ancestor the containing block
 * for fixed-position descendants, so as soon as the header gained its scrolled
 * blur, `top: 61px; bottom: 0` stopped meaning "the viewport" and started
 * meaning "the 61px-tall header". The panel collapsed from 770px to 88px and
 * you could see only a sliver of the menu.
 *
 * Suppressing the blur while the menu is open costs nothing visually — the
 * panel covers the screen anyway — and it keeps one navigation block instead
 * of duplicating the markup for desktop and mobile.
 *
 * The other properties are listed for the same reason: `transform`, `filter`,
 * `perspective`, `contain` and `will-change` all create a containing block
 * too. Anyone adding a slide-in animation to the header would otherwise
 * reintroduce this exact bug, and it looks nothing like a CSS-inheritance
 * problem when it happens. */
.jie-nav-open .jie-header {
  /* Instant, not eased. A transition here reintroduces the jump: the panel's
     containing block does not revert to the viewport until the filter has
     finished animating to none. */
  transition: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  filter: none !important;
  transform: none !important;
  perspective: none !important;
  contain: none !important;
  will-change: auto !important;
}

/* With the blur gone the bar needs to stay opaque, or the menu behind shows
   through it. */
.jie-nav-open .jie-header.is-scrolled {
  background: var(--wp--preset--color--white);
}


/* 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; }

/* The CTA band's phone number, set larger than the header's. */
.jie-phone--lg { font-size: 1.9rem; color: var(--wp--preset--color--cream); }
.jie-phone--lg:hover { color: var(--wp--preset--color--cream); }

.jie-service-area {
  font-size: var(--wp--preset--font-size--small);
  margin-top: var(--wp--preset--spacing--30);
}
