/* =============================================================================
   Section-level composition patterns — used across every page.
   -----------------------------------------------------------------------------
   Section LAYOUTS only. No new colors, sizes, or radii; every value resolves
   to a token in tokens.css. Extracted from index.html so all pages can share.
   ============================================================================= */

/* Image placeholder — flat gray block with descriptive label */
.image-placeholder {
  background: var(--jf-color-bg-alt);
  border: var(--jf-border-width) dashed var(--jf-color-border);
  border-radius: var(--jf-radius-card);
  aspect-ratio: 4 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--jf-space-5);
  text-align: center;
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  font-size: var(--jf-size-small);
  color: var(--jf-color-text);
  line-height: var(--jf-lh-small);
}
.image-placeholder--16-9 { aspect-ratio: 16 / 9; }
.image-placeholder--square { aspect-ratio: 1 / 1; }
.image-placeholder--map { aspect-ratio: 4 / 3; }

/* Image frame — real <img> wrapper that holds the aspect ratio and clips
   the photo with object-fit:cover. Modifiers mirror .image-placeholder so
   swapping a placeholder for a real image is a 1:1 markup change. */
.image-frame {
  display: block;
  overflow: hidden;
  border-radius: var(--jf-radius-card);
  aspect-ratio: 4 / 3;
}
.image-frame--16-9  { aspect-ratio: 16 / 9; }
.image-frame--3-4   { aspect-ratio: 3 / 4; }
.image-frame--square{ aspect-ratio: 1 / 1; }
.image-frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Section — hero, shared scaffolding.

   The homepage hero (.hero--home) and the inner-page hero (.hero--inner)
   share only type rhythm and the CTA row; each sets its own layout. The
   contained 50/50 split that used to live here was replaced by the
   full-bleed treatment below, so nothing renders a two-column hero any more
   and .hero__inner is a single column for everyone. */
.hero { padding-block: var(--jf-space-9); }
.hero__inner {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--jf-space-8);
  align-items: center;
}
.hero__title  { margin-top: var(--jf-space-4); margin-bottom: var(--jf-space-5); }
.hero__lede   {
  margin-bottom: var(--jf-space-6);
  max-width: 46ch;
  font-size: var(--jf-size-lede);
}
.hero__ctas   { display: flex; gap: var(--jf-space-3); flex-wrap: wrap; margin-top: var(--jf-space-3); }


/* -----------------------------------------------------------------------------
   Homepage hero — full-bleed photograph under a navy scrim.

   Replaced a contained 50/50 split with a ~604px image, which read as dated
   and gave the photograph no presence. Homepage only: inner pages keep
   .hero--inner and are deliberately image-free.

   The scrim is the load-bearing part. It holds 0.88 flat across the first 30%
   before falling to 0.30 at the right edge, rather than easing from the very
   left. A gradient that starts falling immediately gives the last line of a
   paragraph a different background from the first, and a contrast measurement
   taken at one point stops describing the rest of the block.

   Measured at 1440 by hiding the copy, screenshotting the hero, and sampling
   the pixels behind each text block — reported against the mean background
   and against the single lightest pixel in each rect, since a mean hides a
   bright patch:
     H1 muted line, white @65%, large   6.28:1 mean / 4.01:1 worst  (needs 3)
     lede, white, 19px normal          13.04:1 mean / 9.99:1 worst  (needs 4.5)
     eyebrow, white, 13px normal       10.79:1 mean / 7.79:1 worst  (needs 4.5)
   Stacked at 1000px the flat 0.90 scrim gives 5.85:1 worst on the muted line.

   No new tokens: the scrim derives its navy from --jf-color-bg-dark through
   color-mix rather than restating the hex.
   ----------------------------------------------------------------------------- */
.hero--home {
  position: relative;
  isolation: isolate;
  display: grid;
  align-items: center;
  min-height: 680px;
  padding-block: var(--jf-space-9);
  /* Painted behind the photograph so a slow or failed image load still gives
     the white type something to sit on. */
  background: var(--jf-color-bg-dark);
}
.hero--home .hero__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -2;
}
.hero--home .hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--jf-color-bg-dark) 88%, transparent) 0%,
    color-mix(in srgb, var(--jf-color-bg-dark) 88%, transparent) 30%,
    color-mix(in srgb, var(--jf-color-bg-dark) 30%, transparent) 100%
  );
}
.hero--home .hero__text { max-width: 620px; }
.hero--home .hero__title,
.hero--home .eyebrow { color: var(--jf-color-text-on-dark); }
.hero--home .hero__title .h1__muted {
  /* Not --jf-color-text-muted-large: that alias is a grey chosen against white and
     goes muddy on navy. White at reduced alpha keeps the hue of the first
     line and stays measurable against the scrim. */
  color: color-mix(in srgb, var(--jf-color-text-on-dark) 65%, transparent);
}
.hero--home .hero__lede { color: var(--jf-color-text-on-dark); }

/* The navy-outline secondary button disappears against a navy scrim. */
.hero--home .btn--secondary {
  background: transparent;
  color: var(--jf-color-text-on-dark);
  border-color: var(--jf-color-text-on-dark);
}
.hero--home .btn--secondary:hover {
  background: var(--jf-color-text-on-dark);
  color: var(--jf-color-bg-dark);
}

@media (max-width: 1024px) {
  .hero--home { min-height: 560px; padding-block: var(--jf-space-8); }
  /* Stacked, the copy sits over the middle of the photograph rather than its
     dark left edge, so the ramp is replaced by a flat, stronger scrim. */
  .hero--home .hero__scrim {
    background: color-mix(in srgb, var(--jf-color-bg-dark) 90%, transparent);
  }
  .hero--home .hero__text { max-width: none; }
}


/* Page hero — inner-page variant. Single column, no image half.

   Width: the inner column was 780px inside a 1272px content area, which
   left 516px of dead white to the right of every inner-page hero and made
   the section read as unfinished. Widened to 1100px, cutting the dead band
   to 172px.

   1100 specifically: swept 980 / 1040 / 1100 / 1140 against all 25 inner
   pages. 1100 is the narrowest width at which service-estate resolves to
   two lines, and no page in the set exceeds three. 1140 buys marginally
   better line counts elsewhere but pushes the display measure past ~32ch
   for no headline that needs it.

   Padding: dropped one step (96px -> 64px) so a short headline plus a
   lede doesn't occupy a mostly-empty screen. */
.hero--inner { padding-block: var(--jf-space-8); }
.hero--inner .hero__inner { max-width: 1100px; }
/* The H1 now fills the hero column rather than carrying its own tighter
   cap. The earlier 21ch cap existed to stop headlines wrapping at an
   arbitrary point inside a 780px column; at 980px the column IS the
   deliberate measure, and a second cap only reintroduced dead space. */
.hero--inner .hero__title { max-width: 100%; }

/* Lede keeps a reading measure of its own — the H1 can fill the column,
   but body-scale copy at 980px would be an uncomfortable line length. */
.hero--inner .hero__lede { max-width: 55ch; }

/* Stacked splits: image on top of text at ≤1024, use a shorter aspect
   so image placeholders don't tower over the content beneath them. */
@media (max-width: 1024px) {
  .split .image-placeholder { aspect-ratio: 16 / 9; }
}

/* Trust band — thin strip between hero and services */
.trust-band {
  background: var(--jf-color-bg-alt);
  border-block: var(--jf-border-width) solid var(--jf-color-border);
  padding-block: var(--jf-space-5);
}
.trust-band__list {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-items: center;
  gap: var(--jf-space-4) var(--jf-space-7);
  list-style: none;
  margin: 0;
  padding: 0;
}
.trust-band__item {
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-small);
  color: var(--jf-color-heading);
  font-weight: var(--jf-weight-medium);
  letter-spacing: 0.02em;
}

/* Prose block — body copy with a comfortable measure. Stack paragraphs
   with body-scale gap between them. */
.prose { max-width: 60ch; }
.prose > p + p { margin-top: var(--jf-space-4); }

/* Long-form article body — measure a bit wider than .prose since articles
   run longer, and it needs h2 / ul / callout treatment .prose doesn't. */
.prose-article {
  max-width: 65ch;
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-body);
  line-height: var(--jf-lh-body);
  color: var(--jf-color-text);
}
/* Tracks the h3 tier by token rather than by a literal, so it moved from 24
   to 28 with change 1 and will keep moving with it — including the mobile
   step, which a hardcoded 28px would have frozen. It stays clearly
   subordinate to the 48px section h2 on the eight service pages that carry
   both: 48 / 28 is 1.71x. */
.prose-article h2 {
  font-family: var(--jf-font-display);
  font-size: var(--jf-size-h3);
  line-height: var(--jf-lh-h3);
  font-weight: var(--jf-weight-semibold);
  color: var(--jf-color-heading);
  margin: var(--jf-space-7) 0 var(--jf-space-3);
}
.prose-article p + p { margin-top: var(--jf-space-4); }
.prose-article ul,
.prose-article ol {
  padding-left: var(--jf-space-5);
  margin: var(--jf-space-4) 0;
}
.prose-article li + li { margin-top: var(--jf-space-2); }
.prose-article strong { color: var(--jf-color-heading); font-weight: var(--jf-weight-semibold); }
.prose-article__callout {
  background: var(--jf-color-bg-alt);
  border-left: 3px solid var(--jf-color-link);
  padding: var(--jf-space-5) var(--jf-space-6);
  margin: var(--jf-space-6) 0;
  border-radius: var(--jf-radius-card);
  font-family: var(--jf-font-body);
  color: var(--jf-color-heading);
}

/* Section header — eyebrow + H2 + optional lede */
.section-head { margin-bottom: var(--jf-space-7); max-width: 60ch; }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head__title { margin-bottom: var(--jf-space-3); }
/* It had no font-size at all, so it rendered at 17px body — the same
   semantic role as .hero__lede at two different sizes depending only on
   where it sat. */
.section-head__lede  { margin: 0; font-size: var(--jf-size-lede); }

/* Service / industry cards — reuse .card and add an icon */
.card__icon {
  width: 48px;
  height: 48px;
  color: var(--jf-color-eyebrow);
  margin-bottom: var(--jf-space-4);
}

/* Stats band — full-bleed navy. Deliberately taller than a regular
   section so it reads as a break in rhythm, not a stripe. */
.stats-band {
  background: var(--jf-color-bg-dark);
  color: var(--jf-color-text-on-dark);
  padding-block: var(--jf-space-10);
}
@media (max-width: 768px) {
  .stats-band { padding-block: var(--jf-space-9); }
}
.stats-band__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--jf-space-6);
}
@media (max-width: 768px) {
  .stats-band__grid { grid-template-columns: repeat(2, 1fr); gap: var(--jf-space-7); }
}
.stat { display: flex; flex-direction: column; gap: var(--jf-space-2); text-align: center; }
.stat__figure {
  font-family: var(--jf-font-display);
  font-size: var(--jf-size-h2);
  line-height: 1;
  font-weight: var(--jf-weight-semibold);
  color: var(--jf-color-text-on-dark);
}
/* The figure was pinned at var(--jf-size-h2) — 48px at every width, the only
   type on the site that never stepped down. In the 2-column mobile grid that
   put "7 Years" at 90% of a 140px column AND broke it onto two lines, while
   the 15px label beside it looked incidental.

   Steps reuse the h2 scale rather than adding a token: h2-tablet (40px) once
   the band is no longer four-across at desktop width, h2-mobile (34px) on
   phones. 34px keeps the longest figure at 82% of its column at 375 with all
   four on one line; 36-40px measured 87-96% and reads as touching the edge.
   Ratio against the 15px label goes 3.20x -> 2.27x, still unambiguous. */
/* 1023, not the usual 1024 compact breakpoint: at 1024 the band is already
   four-across with 220px cells, where the full 48px fits and is wanted. The
   step exists for 769-1023, where four columns get as narrow as 156px and
   48px would overflow "7 Years". */
@media (max-width: 1023px) { .stat__figure { font-size: var(--jf-size-h2-tablet); } }
@media (max-width: 480px)  { .stat__figure { font-size: var(--jf-size-stat-mobile); } }
.stat__label {
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-small);
  font-weight: var(--jf-weight-medium);
  color: var(--jf-color-text-on-dark);
  opacity: 0.75;
  letter-spacing: var(--jf-tracking-eyebrow);
  text-transform: uppercase;
}

/* Split layout — text + media side by side */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--jf-space-8);
  align-items: center;
}
@media (max-width: 1024px) { .split { grid-template-columns: 1fr; } }

/* Checklist */
.checklist {
  list-style: none;
  margin: var(--jf-space-6) 0;
  padding: 0;
  display: grid;
  gap: var(--jf-space-4);
}
.checklist li {
  display: grid;
  grid-template-columns: 20px 1fr;
  gap: var(--jf-space-3);
  align-items: start;
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-body);
  line-height: var(--jf-lh-body);
  color: var(--jf-color-heading);
}
.checklist svg {
  width: 20px;
  height: 20px;
  color: var(--jf-color-link);
  margin-top: var(--jf-space-1);
  flex-shrink: 0;
}

/* Testimonial card */
.testimonial {
  background: var(--jf-color-bg);
  border: var(--jf-border-width) solid var(--jf-color-border);
  border-radius: var(--jf-radius-card);
  padding: var(--jf-space-6);
  display: flex;
  flex-direction: column;
  gap: var(--jf-space-5);
  transition: border-color var(--jf-transition);
}
.testimonial:hover { border-color: var(--jf-color-border-hover); }
.testimonial__quote {
  font-family: var(--jf-font-display);
  font-size: var(--jf-size-h3);
  line-height: var(--jf-lh-h3);
  color: var(--jf-color-heading);
  margin: 0;
  font-style: normal;
}
.testimonial__quote::before {
  content: '\201C';
  display: block;
  font-family: var(--jf-font-display);
  font-size: var(--jf-size-h1);
  line-height: 0.6;
  color: var(--jf-color-eyebrow);
  opacity: 0.4;
  margin-bottom: var(--jf-space-3);
}
.testimonial__attribution {
  display: flex;
  flex-direction: column;
  gap: var(--jf-space-1);
  border-top: var(--jf-border-width) solid var(--jf-color-border);
  padding-top: var(--jf-space-4);
  margin-top: auto;
}
.testimonial__name {
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-body);
  font-weight: var(--jf-weight-semibold);
  color: var(--jf-color-heading);
}
.testimonial__title {
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-small);
  color: var(--jf-color-text);
}
.testimonial__tenure {
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-eyebrow);
  letter-spacing: var(--jf-tracking-eyebrow);
  text-transform: uppercase;
  font-weight: var(--jf-weight-semibold);
  color: var(--jf-color-eyebrow);
  margin-top: var(--jf-space-2);
}

/* Article card (insights) */
.article-card {
  display: flex;
  flex-direction: column;
  background: var(--jf-color-bg);
  border: var(--jf-border-width) solid var(--jf-color-border);
  border-radius: var(--jf-radius-card);
  overflow: hidden;
  transition: border-color var(--jf-transition);
}
.article-card:hover { border-color: var(--jf-color-border-hover); }
.article-card .image-placeholder,
.article-card .article-card__fallback,
.article-card .image-frame {
  border: 0;
  border-radius: 0;
  border-bottom: var(--jf-border-width) solid var(--jf-color-border);
}
.article-card__body {
  padding: var(--jf-space-5);
  display: flex;
  flex-direction: column;
  gap: var(--jf-space-3);
  flex: 1;
}
.article-card__meta {
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-eyebrow);
  letter-spacing: var(--jf-tracking-eyebrow);
  text-transform: uppercase;
  font-weight: var(--jf-weight-semibold);
  color: var(--jf-color-eyebrow);
  margin: 0;
}
.article-card__title {
  font-family: var(--jf-font-display);
  font-size: var(--jf-size-card-title);
  line-height: var(--jf-lh-h3);
  color: var(--jf-color-heading);
  margin: 0;
}
.article-card .card__link { margin-top: auto; padding-top: var(--jf-space-3); }

/* Card media stand-in, for a post published with no featured image.

   Featured images are optional on purpose: requiring one means a member of
   staff has to source a photograph before they can publish a 400-word note,
   which is how a firm's writing section stops being updated. This has to look
   like a decision rather than a gap, so it is a navy block carrying the topic
   — not a grey box, and not the "IMAGE:" placeholder the prototype uses to
   mark work still to be commissioned. */
.article-card__fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--jf-space-5);
  background: var(--jf-color-bg-dark);
  text-align: center;
}
.article-card__fallback-label {
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-eyebrow);
  letter-spacing: var(--jf-tracking-eyebrow);
  text-transform: uppercase;
  font-weight: var(--jf-weight-semibold);
  color: var(--jf-color-text-on-dark);
}

/* Topic filter under the archive grid. The category on a card's meta line is
   not a link, so without this the category archives have no route in. */
.topic-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--jf-space-3) var(--jf-space-5);
  margin-top: var(--jf-space-7);
  padding-top: var(--jf-space-5);
  border-top: var(--jf-border-width) solid var(--jf-color-border);
}
.topic-links a {
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-small);
  color: var(--jf-color-link);
  /* 44px tap target without changing the visual line height. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.topic-links a[aria-current="page"] {
  color: var(--jf-color-heading);
  font-weight: var(--jf-weight-semibold);
  text-decoration: none;
}

/* Archive pagination. Invisible until there are more than nine articles, and
   wrong on the day there are if it is not built now. */
.pagination { margin-top: var(--jf-space-7); }
.pagination__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--jf-space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.pagination__list a,
.pagination__list span {
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-small);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--jf-space-3);
  border: var(--jf-border-width) solid var(--jf-color-border);
  border-radius: var(--jf-radius-card);
  color: var(--jf-color-link);
  text-decoration: none;
}
.pagination__list a:hover { border-color: var(--jf-color-border-hover); }
.pagination__list .current {
  color: var(--jf-color-text-on-dark);
  background: var(--jf-color-bg-dark);
  border-color: var(--jf-color-bg-dark);
}

.article-grid__empty {
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-body);
  color: var(--jf-color-text);
}

.cta-row { margin-top: var(--jf-space-7); text-align: center; }

/* Local section — hours block below NAP */
.local__hours { margin-top: var(--jf-space-6); }
.local__hours-title {
  font-family: var(--jf-font-body);
  font-size: var(--jf-size-eyebrow);
  letter-spacing: var(--jf-tracking-eyebrow);
  text-transform: uppercase;
  font-weight: var(--jf-weight-semibold);
  color: var(--jf-color-eyebrow);
  margin: 0 0 var(--jf-space-2);
}
