@charset "UTF-8";
/* =========================================================================
   finalert.com — pages.css
   Stage 2: the `page` post type. Built from SPEC-P1-about.md,
   SPEC-P2-utility.md and SPEC-P3-legal.md, all inheriting SPEC-00.

   Loads AFTER assets/next.css and depends on it for every token, every
   utility (.container, .eyebrow, .tick, .btn*, .link-arrow, .r*, .sec__head,
   .stats, .pillars, .field__*, .loc__*, .cta-band, .ft*) and for the
   @keyframes it reuses (hero-up, grow-y). It defines no token of its own and
   no parallel copy of any component that already exists.

   Do not add a section without its approved spec.
   ========================================================================= */

/* =========================================================================
   SHARED PAGE CHROME  (SPEC-P1 §4)
   ========================================================================= */

/* --- interior header: solid from load (SPEC-P1 §4.1) ---------------------
   next.css gives .site-header--solid the stuck bar's translucent fill plus a
   backdrop-filter. On an interior page there is no dark or media hero under
   it and no state change to justify the blur, so §4.1's preferred outcome
   applies: the bar goes opaque --paper and the page spends ZERO of SPEC-00
   §8.6's three backdrop-filter elements. SPEC-P2 §1 and SPEC-P3 §A6 both
   budget 0 and this is what makes that true. The homepage is untouched: it
   ships --float and reaches .is-stuck, which keeps its own recipe. */
.site-header--solid {
  background: var(--paper);
  -webkit-backdrop-filter: none;
          backdrop-filter: none;
}


/* --- breadcrumb (SPEC-P1 §4.3) ------------------------------------------
   Two levels only. The separator is generated content on the <li>, never a
   character in the markup, so it is not read aloud. The terminal item is not
   a link: a link to the page you are on is a dead control. */
.phero__crumb ol { display: flex; flex-wrap: wrap; align-items: center; }
.phero__crumb li { display: inline-flex; align-items: center; min-height: 44px; font: var(--t-cap); color: var(--ink-3); }
.phero__crumb li + li::before { content: "\203A"; margin-inline: var(--s-2); color: var(--navy-300); }
/* SPEC-00 §7.4 — the 44x44 target comes from padding, never a transform. The
   negative inline-start margin pulls the only link's glyphs back flush with
   the H1 and the eyebrow below it, so the hit area grows and the optical
   left edge of the page does not move. */
.phero__crumb a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--s-2);
  margin-inline-start: calc(var(--s-2) * -1);
  color: var(--navy);
  text-decoration: none;
}
.phero__crumb a:hover { color: var(--navy-500); text-decoration: underline; text-underline-offset: 3px; }
.phero__crumb + .eyebrow { margin-top: var(--s-3); }

/* =========================================================================
   THE INTERIOR HERO, "THE LEDGER HEAD"  (SPEC-IH-R3)

   One base and four modifiers replace .phero--field, .phero--plate, .svchero,
   .hubhero, .insahero, .pohero and .cshero's deltas. An interior page opens on
   PAPER, not navy: the navy field, the calc(50vw + Npx) gold seam, the
   full-bleed half plane and the 88px two-tone headline are the homepage's four
   exclusive devices and appear on no other template (§8).

     .ihero--hub      contents index in the right track, no photograph
     .ihero--single   photo plate + index bar of jump anchors or facts
     .ihero--archive  photo plate + index bar of categories with aria-current
     .ihero--post     photo plate, no index bar
     .ihero--plate    title only: legal, 404, thank-you, sitemap

   The header above is the 72px opaque bar carrying its own 1px bottom rule
   (§11.1); the record strip's rule sits 44px below it and the pair reads as
   ledger ruling. Zero backdrop-filter, zero navy bands, one gold at rest.

   .ihero must NOT carry `overflow: clip`: the photo plate drops through the
   band's bottom edge and the clip would eat the drop (§4.2).
   ========================================================================= */
.ihero {
  position: relative;
  isolation: isolate;
  background: linear-gradient(180deg, var(--wash) 0, var(--paper) 240px);
  color: var(--ink);
  /* the aside track and the column gap, one pair of knobs for the body grid
     and for the index bar's reserved right track, which is the plate's
     landing rather than empty space (§4.3) */
  --ihero-aside: 100%;
  --ihero-gap: 0px;
  --ihero-pt: var(--s-7);
  --ihero-pb: var(--s-8);
  --plate-h: auto;
  --plate-drop: 0px;
}

/* the 96px grid, in the LIGHT stroke, masked to the top-right shoulder of the
   aside track only. On a photo variant the plate covers nearly all of it. */
.ihero__pattern {
  position: absolute;
  inset: 0;
  z-index: var(--z-pattern);
  pointer-events: none;
  background-image: var(--pat-grid);
  background-size: 96px 96px;
  --pat-x: 96%; --pat-y: 0%; --pat-r: 34% 70%;   /* tightened after render review: the 64% 88% ellipse ruled straight through the copy column */
  -webkit-mask-image: radial-gradient(var(--pat-r) at var(--pat-x) var(--pat-y), #000 0 38%, transparent 82%);
          mask-image: radial-gradient(var(--pat-r) at var(--pat-x) var(--pat-y), #000 0 38%, transparent 82%);
}
@media (max-width: 767px) { .ihero__pattern { display: none; } }
/* DEVIATION, LOGGED. §6 moves the mask to `at 92% 6%` on the two plate-less
   variants and keeps the 64% x 88% radius. Measured at 1440 that ellipse runs
   from x=627 to the viewport edge, so on --hub it paints 96px ruling straight
   through the contents rows and the two rule systems fight. On a photo variant
   the plate covers the same wash, which is the "almost entirely hidden behind
   the plate" §6 describes; with no plate there is nothing to cover it. The
   radius is cut here ONLY, so the pattern stays the corner texture §6 asks for
   and clears the contents track. Photo variants keep §6's numbers exactly. */
.ihero--hub .ihero__pattern,
.ihero--plate .ihero__pattern { --pat-x: 92%; --pat-y: 6%; --pat-r: 26% 54%; }

/* --- the record strip (§4.1) ---------------------------------------------
   A full-bleed 1px rule carrying the breadcrumb. .phero__crumb supplies the
   box verbatim; the per-hero navy recolours are deleted with the navy. The
   strip is 44px at every width rather than the sketch's 40 below 768, because
   the crumb link's 44px tap target (§10.4) sets the floor and a tap target is
   not negotiable against four pixels. */
.ihero__strip {
  position: relative;
  z-index: var(--z-content);
  border-bottom: 1px solid var(--line);
}
.ihero__strip > .container { display: flex; align-items: center; min-height: 44px; }
.ihero__crumb + .eyebrow { margin-top: 0; }

/* SPEC-SI-R3 §4.1.4 | the filed-under chip, the ONE element that spec adds to
   this hero's markup, and it is optional at the strip level, so an .ihero
   without it is byte-identical to SPEC-IH's own.

   `service-type` is 89 of 100 on the service post type and has three terms. The
   chip is inside the strip and OUTSIDE the breadcrumb <nav>, so it is not
   announced as part of the trail, and it is a <span> with the same box and no
   hover until the term archive has a v3 template. */
.ihero__striprow { justify-content: space-between; gap: var(--s-4); }
.ihero__filed {
  display: flex; align-items: center; gap: var(--s-2);
  flex: 0 0 auto;
  font: 400 13px/1.4 var(--font-ui);
  color: var(--ink-3);
}
.ihero__chip {
  display: inline-flex; align-items: center;
  padding: 3px 10px;
  border: 1px solid var(--navy-100);
  border-radius: var(--r-pill);
  background: var(--navy-050);
  font: 600 13px/1.4 var(--font-ui);
  color: var(--navy);                    /* 12.9:1 on --navy-050 */
  text-decoration: none;
  transition: border-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
a.ihero__chip:hover { border-color: var(--navy-300); color: var(--navy-500); }
@media (max-width: 560px) {
  /* the chip wraps to its own row and the strip grows from 44 to 64 rather than
     squeezing a breadcrumb that already owns a 44px tap target */
  .ihero__striprow { flex-wrap: wrap; row-gap: 6px; padding-block: 8px; }
}

/* --- the body grid (§4.1) ------------------------------------------------ */
.ihero__grid {
  position: relative;
  z-index: var(--z-content);
  display: grid;
  padding-block: var(--ihero-pt) var(--ihero-pb);
}
.ihero__copy { min-width: 0; }
.ihero__aside { min-width: 0; }

@media (min-width: 561px) { .ihero { --ihero-pt: var(--s-8); --ihero-pb: var(--s-9); } }
@media (min-width: 1024px) {
  .ihero {
    --ihero-aside: 340px;
    --ihero-gap: 40px;
    --ihero-pt: 44px;
    --ihero-pb: 48px;
    --plate-h: 300px;
    --plate-drop: 56px;
  }
  .ihero__grid {
    grid-template-columns: minmax(0, 1fr) var(--ihero-aside);
    column-gap: var(--ihero-gap);
    align-items: start;
  }
  /* a two-word h1 with no lede cannot let the plate's top edge climb above the
     eyebrow. Photo variants only: --hub has no plate and --plate has no grid,
     and a floor on either would inflate a band with nothing to hold up. */
  :is(.ihero--single, .ihero--archive, .ihero--post) .ihero__grid {
    min-height: calc(var(--plate-h) - var(--plate-drop) + var(--s-6));
  }
  .ihero--noplate .ihero__grid { min-height: 0; }
}
@media (min-width: 1180px) {
  .ihero {
    --ihero-aside: clamp(400px, 34vw, 496px);
    --ihero-gap: clamp(48px, 4.4vw, 72px);
    --ihero-pt: var(--s-9);
    --ihero-pb: 56px;
    --plate-h: clamp(360px, 30vw, 432px);
    --plate-drop: 72px;
  }
}

/* --- type (§5) ----------------------------------------------------------- */
.ihero__eyebrow { color: var(--navy); }
.ihero__eyebrow .tick { width: 28px; }

.ihero__h1 {
  margin-top: var(--s-4);
  font: var(--t-d1);
  letter-spacing: -.024em;
  color: var(--ink);
  overflow-wrap: break-word;
}
/* the two-tone rule survives; its second colour is --navy at 13.7:1, because
   gold on paper is 1.5:1 and forbidden (§5.1) */
.ihero__accent { color: var(--navy); }

/* a 3px navy rule with 20px of padding, not a box */
.ihero__who {
  position: relative;
  margin-top: var(--s-6);
  padding-left: var(--s-5);
  font: 600 16px/1.55 var(--font-ui);
  color: var(--ink);
  max-width: 44ch;
}
.ihero__who::before {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--navy);
  transform: scaleY(0); transform-origin: top;
  animation: grow-y 380ms var(--ease-out) 260ms both;
}
.ihero__lead {
  margin-top: var(--s-6);
  font: var(--t-lead);
  color: var(--ink-3);
  max-width: 52ch;
  text-wrap: pretty;
}
.ihero--single .ihero__lead { max-width: 46ch; }
/* with no who-it-is-for line the h1-to-lede gap grows and nothing is
   substituted */
.ihero__lead--gap { margin-top: var(--s-7); }

.ihero__actions {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s-5) var(--s-7);
  margin-top: var(--s-8);
}
/* .link-arrow--on-dark carries a 32px top margin for the stacked band it was
   written for; inside this flex row it would leave the buttons' baseline. */
.ihero__actions .link-arrow { margin-top: 0; }

/* the fact line leaves the rail and lands under the actions, on its own rule */
.ihero__fact {
  margin-top: var(--s-6);
  padding-top: var(--s-5);
  border-top: 1px solid var(--line);
  font: var(--t-cap);
  color: var(--ink-3);
  max-width: 46ch;
}
.ihero__fact .link-arrow { margin-top: 0; }

/* --- the photo plate (§4.2) ----------------------------------------------
   Contained, rounded and anchored to the BOTTOM of the body, so a longer or
   shorter copy column moves its top edge and never its drop. `overflow: clip`
   sits here and not on .ihero. */
.ihero__plate {
  position: relative;
  z-index: 2;
  margin-top: var(--s-7);
  border-radius: var(--r-md);
  overflow: clip;
  aspect-ratio: 3 / 2;
  animation: plate-in 620ms var(--ease-soft) 240ms both;
}
.ihero__plate picture { display: block; height: 100%; }
.ihero__plate img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: var(--po, 50% 44%);
}
@media (min-width: 1024px) {
  /* the ASIDE is the grid item, so the drop lives on it; putting align-self on
     the plate would be a no-op inside a block parent.

     align-self: START, not END. Bottom-anchoring was the defect the client
     reported: a tall copy column (eyebrow, h1, who-line, a whole lede, actions
     and the fact line) pushed the plate's top edge hundreds of pixels down the
     right track, leaving a grid-pattern void above it and an image that reads
     as having fallen down the page. Anchored to the top, the plate's top edge
     sits on the eyebrow's and the band's height is driven by whichever column
     is taller.

     The negative bottom margin is unchanged and still produces the drop: the
     aside occupies (plate height - drop - padding-bottom) in the grid, so when
     the plate is the taller column it overhangs the band's bottom edge by
     exactly --plate-drop and lands on the index-bar shelf. When the copy is
     taller the plate simply sits at the top and does not drop, which is the
     honest result and the one that has no void in it. */
  :is(.ihero--single, .ihero--archive, .ihero--post) .ihero__aside {
    align-self: start;
    margin-bottom: calc((var(--plate-drop) + var(--ihero-pb)) * -1);
  }
  .ihero__plate {
    margin-top: 0;
    height: var(--plate-h);
    aspect-ratio: auto;
    border-radius: var(--r-lg);
  }
}

/* --- the index bar (§4.3) ------------------------------------------------
   The spine rail's job, reborn as ruled ink on a --navy-050 shelf. No glass,
   no blur, no --rail-n: auto-fit handles 1 to 6 cells and 0 cells renders no
   bar at all. */
.ihero__ix {
  position: relative;
  z-index: 1;
  background: var(--navy-050);
  border-bottom: 1px solid var(--navy-100);
  animation: hero-up 440ms var(--ease-out) 420ms both;
  --hy: 12px;
}
.ihero__ix-in { display: grid; }
.ihero__cells {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  border-top: 1px solid var(--navy-100);
  list-style: none;
  margin: 0; padding: 0;
}
.ihero__cell { position: relative; min-width: 0; }
.ihero__cell + .ihero__cell { border-top: 1px solid var(--navy-100); }
.ihero__link,
.ihero__cell > .ihero__idx {
  display: block;
  text-decoration: none;
}
.ihero__link {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 64px;
  padding-block: var(--s-3);
}
.ihero__idx {
  display: block;
  font: 600 11px/1 var(--font-ui);
  font-variant-numeric: tabular-nums;
  letter-spacing: .20em;
  color: var(--ink-3);
  margin-bottom: 6px;
}
.ihero__lbl {
  display: block;
  font: 600 16px/1.2 var(--font-display);
  letter-spacing: -.012em;
  color: var(--navy);
  transition: color var(--dur-2) var(--ease-out);
}
.ihero__sub { display: block; margin-top: 3px; font: var(--t-cap); color: var(--ink-3); }
.ihero__num {
  display: block;
  font: 600 30px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.030em;
  color: var(--navy);
}
/* gold on --navy-050 is ~1.4:1 and the + / % glyph carries meaning, so it
   cannot be decorative (§12.2) */
.ihero__suffix { color: var(--navy-500); font-size: .62em; vertical-align: .28em; }
.ihero__cap {
  display: block;
  margin-top: 6px;
  font: 400 13px/1.42 var(--font-ui);
  color: var(--ink-3);
  max-width: 22ch;
}
/* a case-study fact cell: a <dl>, never a <nav> */
.ihero__cell dt {
  font: 600 11px/1 var(--font-display);
  letter-spacing: .20em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.ihero__cell dd {
  margin: 6px 0 0;
  font: 600 17px/1.25 var(--font-display);
  letter-spacing: -.012em;
  color: var(--navy);
  overflow-wrap: anywhere;
}
.ihero__cell--long dd { font: 600 14px/1.35 var(--font-ui); letter-spacing: 0; color: var(--ink); }
.ihero__cell dl,
.ihero__cells > .ihero__cell > div { min-width: 0; }
/* the stat and fact cells are not links and take the same 64px floor */
.ihero__cell > .ihero__stat,
.ihero__cell > div { display: block; padding-block: var(--s-3); min-height: 64px; }

/* the 2px gold underline: a STATE, never a resting decoration, except on the
   one aria-current cell of the archive */
.ihero__link::after {
  content: "";
  position: absolute; left: 0; bottom: 0;
  width: calc(100% - var(--s-5)); height: 2px;
  background: var(--gold);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur-2) var(--ease-out);
}
.ihero__link:hover::after,
.ihero__link:focus-visible::after,
.ihero__link[aria-current="page"]::after { transform: scaleX(1); }
.ihero__link:hover .ihero__lbl,
.ihero__link:focus-visible .ihero__lbl { color: var(--navy-500); }
/* the gold underline is never the sole signal (§10.7) */
.ihero__link[aria-current="page"] .ihero__lbl { font-weight: 700; color: var(--navy); }

@media (min-width: 561px) {
  .ihero__cells { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ihero__cell + .ihero__cell { border-top: 0; }
  .ihero__cell:nth-child(n + 3) { border-top: 1px solid var(--navy-100); }
}
@media (min-width: 1024px) {
  .ihero__ix { animation-name: hero-up; }
  .ihero__ix-in {
    grid-template-columns: minmax(0, 1fr) calc(var(--ihero-aside) + var(--ihero-gap));
    align-items: stretch;
  }
  .ihero__cells {
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    min-height: 84px;
  }
  .ihero__cell + .ihero__cell,
  .ihero__cell:nth-child(n + 3) { border-top: 0; }
  .ihero__cell + .ihero__cell { padding-left: var(--s-6); }
  .ihero__cell + .ihero__cell::before {
    content: "";
    position: absolute; left: 0; top: 18px; bottom: 18px;
    width: 1px;
    background: var(--navy-100);
    transform: scaleY(0); transform-origin: top;
    animation: grow-y 320ms var(--ease-out) calc(500ms + var(--i, 0) * 60ms) both;
  }
  .ihero__link,
  .ihero__cell > .ihero__stat,
  .ihero__cell > div { min-height: 84px; padding-block: var(--s-3); padding-right: var(--s-5); }
}
@media (min-width: 1180px) {
  .ihero__cells { grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); min-height: 88px; }
  .ihero__link,
  .ihero__cell > .ihero__stat,
  .ihero__cell > div { min-height: 88px; }
  .ihero__lbl { font-size: 17px; }
  .ihero__num { font-size: 34px; }
}

/* --- the post variant (§5, §12.1) ---------------------------------------- */
.ihero--post .ihero__h1 {
  font: var(--t-d1);
  letter-spacing: -.022em;
  max-width: 22ch;
}
.ihero__meta {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s-2) var(--s-3);
  margin-top: var(--s-5);
  padding-top: var(--s-5);
  border-top: 1px solid var(--line);
  font: 400 13px/1.5 var(--font-ui);
  color: var(--ink-3);
}
.ihero__meta--gap { margin-top: var(--s-8); }
.ihero__cat {
  display: inline-flex; align-items: center; gap: var(--s-2);
  min-height: 44px;
  font: 600 12px/1.4 var(--font-display);
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--navy);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  text-decoration-color: var(--navy-300);
  transition: color var(--dur-1) var(--ease-out),
              text-decoration-color var(--dur-1) var(--ease-out);
}
.ihero__cat svg { width: 14px; height: 14px; }
.ihero__cat:hover { color: var(--navy-500); text-decoration-color: var(--navy-500); text-decoration-thickness: 1.5px; }
.ihero__sep { width: 4px; height: 4px; border-radius: 50%; background: var(--navy-300); }

/* --- the plate variant (§3e) ---------------------------------------------
   Title only: no photograph, no aside, no shelf, no index bar. */
.ihero--plate .ihero__grid { display: block; padding-block: clamp(40px, 4.4vw, 64px) clamp(44px, 4.8vw, 72px); }
.ihero--plate .ihero__h1 {
  font: var(--t-d1);
  letter-spacing: -.022em;
  max-width: 20ch;
  text-wrap: balance;
}
.ihero--center { text-align: center; }
.ihero--center .ihero__copy { max-width: var(--container-narrow); margin-inline: auto; }
.ihero--center .ihero__h1,
.ihero--center .ihero__lead { margin-inline: auto; }
.ihero--center .eyebrow { justify-content: center; }

/* --- the hub variant (§3a) -----------------------------------------------
   No photograph. The right track is the contents index, .hubtoc* verbatim and
   recoloured for paper. A hub with zero rows renders no aside at all (§7.7). */
.ihero--hub .ihero__aside { margin-top: var(--s-8); }
@media (min-width: 1024px) { .ihero--hub .ihero__aside { margin-top: 0; } }
.ihero--hub .ihero__copy:only-child { max-width: 72ch; }

/* an unresolved case-study sector: no plate, no empty right track (§13.3) */
.ihero--noplate .ihero__grid { display: block; }
.ihero--noplate .ihero__copy { max-width: 72ch; }

/* the case-study title is a sentence, not a two-word service name */
.ihero--cs .ihero__h1 {
  font: var(--t-d1);
  letter-spacing: -.024em;
  text-wrap: balance;
}

/* --- focus (§10.5) -------------------------------------------------------
   There is no navy field left in this hero, so the white/gold ring pair that
   .phero--field and .svchero carried is deleted. The .svc-cta and .pl-cta
   copies stay: those bands are still navy. */
.ihero :focus-visible {
  outline: 2px solid var(--navy-500);
  outline-offset: 2px;
}

/* --- load choreography (§9.1). Pure CSS keyframes with `both`, so the hero
   renders complete with JS disabled. Last frame 880ms; the homepage's is
   1,440ms. plane-in, plane-img, grow-x on a seam and the .ln cascade are the
   homepage's motion signature and are NOT used here. */
@keyframes plate-in { from { opacity: 0; transform: scale(1.015); } to { opacity: 1; transform: none; } }

.ihero .hl { animation: hero-up var(--hdur, 460ms) var(--ease-out) var(--hd, 0ms) both; }
.ihero .ihero__crumb   { --hy:  8px; --hdur: 320ms; --hd:   0ms; }
.ihero .ihero__eyebrow { --hy:  8px; --hdur: 320ms; --hd:  60ms; }
.ihero .ihero__h1      { --hy: 14px; --hdur: 460ms; --hd: 120ms; }
.ihero .ihero__who,
.ihero .ihero__lead    { --hy: 14px; --hdur: 460ms; --hd: 200ms; }
.ihero .ihero__actions { --hy: 12px; --hdur: 420ms; --hd: 280ms; }
.ihero .ihero__meta    { --hy: 12px; --hdur: 420ms; --hd: 260ms; }
.ihero .ihero__fact    { --hy:  0px; --hdur: 360ms; --hd: 320ms; }

/* --- reduced motion (§9.4) -----------------------------------------------
   Stated in full rather than inherited: the plate, the dividers and the
   contents rows all rest at a non-final value and next.css's global .01ms
   collapse is a safety net, not a contract. Hover STATES remain; only their
   interpolation is dropped. */
@media (prefers-reduced-motion: reduce) {
  .ihero *, .ihero__ix * { animation: none !important; transition: none !important; }
  .ihero .hl,
  .ihero__ix { opacity: 1 !important; transform: none !important; }
  .ihero__plate { opacity: 1; transform: none; }
  .ihero__who::before,
  .ihero__cell + .ihero__cell::before { transform: none !important; }
  .hubtoc__item, .hubtoc__label { opacity: 1 !important; transform: none !important; }
  /* the current archive cell's underline rests DRAWN and must stay drawn */
  .ihero__link[aria-current="page"]::after { transform: scaleX(1) !important; }
  .ihero__link::after { transform: scaleX(0); }
}

/* --- prefers-contrast: more (§10.9) -------------------------------------- */
@media (prefers-contrast: more) {
  .ihero__strip { border-bottom-color: var(--navy-300); }
  .ihero__ix { border-bottom-color: var(--navy-300); }
  .ihero__cells,
  .ihero__cell + .ihero__cell,
  .ihero__cell:nth-child(n + 3) { border-top-color: var(--navy-300); }
  .ihero__cell + .ihero__cell::before { background: var(--navy-300); }
  .ihero__plate { outline: 1px solid var(--navy-100); }
  .ihero__pattern { display: none; }
}

/* =========================================================================
   ABOUT  (SPEC-P1)
   ========================================================================= */

/* --- 2. the story band (SPEC-P-R2 §3.4) ---------------------------------
   7/5 with the photograph as a COLUMN PLATE: it fills its track, it takes
   --r-md, and it carries no outline, no shadow and no gold bar. The
   .frame__gold stub is deleted with the frame; a 5px gold rule on a
   photograph corner was the "arsenal of small ideas" SPEC-01-R2 §11 named. */
.ab-story { padding-block: var(--section-y); background: var(--paper); }

.ab-story__grid { display: grid; gap: var(--s-8); }
@media (min-width: 1024px) {
  .ab-story__grid {
    grid-template-columns: minmax(0, 1fr) 440px;
    column-gap: var(--s-12);
    align-items: start;
  }
}

.ab-story__h {
  margin-top: var(--s-3);
  font: var(--t-d2);
  letter-spacing: -.022em;
  color: var(--ink);
  max-width: 22ch;
  text-wrap: balance;
}
/* paragraph 1 is promoted to a standfirst; 2 and 3 are body */
.ab-story__stand {
  margin-top: var(--s-6);
  font: var(--t-d3);
  font-weight: 400;
  color: var(--ink);
  max-width: 34ch;
  text-wrap: pretty;
}
.ab-story__copy p.ab-story__body {
  margin-top: var(--s-5);
  font: var(--t-body);
  color: var(--ink-2);
  max-width: 58ch;
  text-wrap: pretty;
}
.ab-story__copy p.ab-story__body + p.ab-story__body { margin-top: var(--s-5); }

/* the column plate (SPEC-P-R2 §2.5 row 3): fills its track, no outline, no
   shadow, no radius of its own beyond --r-md */
.ab-story__plate {
  position: relative;
  z-index: var(--z-media);
  margin: 0;
  aspect-ratio: 3 / 2;                 /* ≤767 */
  overflow: clip;
  border-radius: var(--r-sm);
  background: var(--navy-050);
}
.ab-story__plate picture { position: absolute; inset: 0; display: block; }
.ab-story__plate img { width: 100%; height: 100%; object-fit: cover; object-position: var(--po, 50% 44%); }
@media (min-width: 768px)  { .ab-story__plate { border-radius: var(--r-md); margin-top: var(--s-9); } }
@media (min-width: 1024px) { .ab-story__plate { aspect-ratio: 4 / 3; margin-top: 0; align-self: start; } }

/* --- 3. the three disciplines (SPEC-P-R2 §3.5) ---------------------------
   A --wash section holding a full-bleed --paper panel with a 72x3 gold mark on
   its top rule: SPEC-04-R2's device, used here for the same reason. What were
   three bordered cards are now three columns of ONE panel, separated by 1px
   rules, and the shared svc-*-lg.png glyph is capped at 112px so a phone does
   not spend 900px of scroll on three line drawings (defect 19). */
.ab-disc { background: var(--wash); padding-block: var(--section-y); overflow: clip; }
/* 2026-09-20: the panel carries the wash tone and the three disciplines are
   --paper cards on it, so each card's mini-ledger sits on its own surface. */
.ab-disc__panel {
  position: relative;
  padding-block: var(--s-10);
  background: var(--navy-050);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.ab-disc__panel::before {
  content: "";
  position: absolute;
  top: -1px;
  left: max(var(--gutter), calc(50% - (var(--container) / 2) + var(--gutter)));
  width: 56px; height: 3px;
  background: var(--gold);
}
.ab-disc__grid { display: grid; }
.ab-disc__h {
  margin-top: var(--s-3);
  font: var(--t-d2);
  letter-spacing: -.022em;
  color: var(--ink);
  max-width: 16ch;
  text-wrap: balance;
}
.ab-disc__cols { display: grid; gap: var(--s-5); margin-top: var(--s-9); }
/* cards, not ruled columns: --paper, one hairline, and a column flex box so
   the ledger can be pinned to the foot and the three heights match. */
.ab-disc__col {
  display: flex;
  flex-direction: column;
  padding: var(--s-6);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.ab-disc__ledger { margin-top: auto; padding-top: var(--s-5); }
.ab-disc__ledger .pillar__row { padding-block: var(--s-3); }
.ab-disc__idx {
  display: block;
  font: 600 11px/1 var(--font-ui);
  font-variant-numeric: tabular-nums;
  letter-spacing: .20em;
  color: var(--navy-300);
}
/* the pillar illustration, mounted as the homepage mounts it (next.css
   .pillar__plate): one --paper frame with the 1px --line inset ring, the
   880x880 art filling a fixed-height window, and the same per-subject
   object-position and scale that bring three differently framed
   illustrations to one apparent subject size. A fixed height rather than an
   aspect-ratio, because the three columns carry unequal padding and three
   plates of three heights would read as a mistake. No new asset. */
.ab-disc__plate {
  display: block;
  width: 128px; height: 128px;    /* icon scale, same tile as the homepage pillars */
  margin-top: var(--s-4);
  background: var(--paper);
  border-radius: var(--r-md);
  box-shadow: 0 0 0 1px var(--line);
  overflow: hidden;
}
.ab-disc__plate img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }
.ab-disc__col:nth-child(2) .ab-disc__plate img,
.ab-disc__col:nth-child(3) .ab-disc__plate img { scale: 1.12; }
.ab-disc__title { margin-top: var(--s-5); font: var(--t-d4); color: var(--navy); }
.ab-disc__text  { margin-top: var(--s-3); font: var(--t-body-sm); color: var(--ink-3); max-width: 34ch; }


@media (min-width: 768px) { .ab-disc__panel { padding-block: var(--s-11); } }
@media (min-width: 1024px) {
  .ab-disc__panel::before { width: 72px; }
  .ab-disc__grid { grid-template-columns: 320px minmax(0, 1fr); column-gap: var(--s-10); }
  .ab-disc__cols { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-6); margin-top: 0; align-items: stretch; }
  /* 190 at 1360, capped either side */
  .ab-disc__plate { width: 128px; height: 128px; }
}
@media (min-width: 1180px) {
  .ab-disc__grid { grid-template-columns: 380px minmax(0, 1fr); column-gap: var(--s-11); }
}

/* --- 4. the values ledger (SPEC-P-R2 §3.6) -------------------------------
   No new component: this band renders the SPEC-S1-R2 §6 ledger markup
   (.svc-del__grid / __aside / __list / __item / __num / __title / __text) and
   the scope of those rules is extended to .page-about further down the file.
   .vals, .val, .val__num and the 2x2 grid are deleted outright. */
.ab-work { background: var(--paper); padding-block: var(--section-y); }

/* --- 5. the offices ledger (SPEC-P-R2 §3.8) ------------------------------
   Two ruled rows, not two bordered cards, and NO photograph under the heading
   that names two street addresses: inc/photos.php lines 17-27 forbid
   captioning a library photograph as the firm's premises and SPEC-08-R2 §1
   already made that ruling for the homepage's offices band. The band closes on
   a decorative bled plate with alt="" and no caption, which asserts nothing.
   .loc--plain and .ab-off__grid are deleted. */
/* padding-bottom is ZERO: the closing plate below is the section's own bottom
   edge, so the band ends on the photograph and meets the navy CTA at a hard
   edge. With the section's usual 128px under it the plate read as a stranded
   strip in the middle of a white gap. */
.ab-off { padding-block: var(--section-y) 0; background: var(--paper); }
.ab-off__list { margin-top: var(--s-8); border-top: 1px solid var(--line); }
.ab-off__row {
  display: grid;
  gap: var(--s-3);
  padding-block: var(--s-6);
  border-bottom: 1px solid var(--line);
}
.ab-off__flag {
  justify-self: start;
  padding: 6px 10px;
  font: var(--t-label);
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--navy);                  /* 12.0:1 on --navy-050 */
  background: var(--navy-050);
  border-radius: var(--r-xs);
}
.ab-off__city { font: 600 22px/1.26 var(--font-display); letter-spacing: -.014em; color: var(--navy); }
.ab-off__addr {
  font: var(--t-body-sm);
  line-height: 1.62;
  font-style: normal;                  /* SPEC-00 §1.3 bans italics */
  color: var(--ink-3);
}
.ab-off__contact { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5); }
.ab-off__contact a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font: var(--t-body-sm);
  color: var(--navy);
  text-decoration: none;
}
.ab-off__contact a:hover {
  color: var(--navy-500);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1.5px;
}
@media (min-width: 768px) {
  .ab-off__row {
    grid-template-columns: 148px minmax(0, 1fr);
    column-gap: var(--s-6);
    padding-block: var(--s-7);
    align-items: start;
  }
  .ab-off__flag { grid-column: 1; grid-row: 1; }
  .ab-off__body { grid-column: 2; grid-row: 1; }
  .ab-off__det  { grid-column: 2; grid-row: 2; }
}
@media (min-width: 1024px) {
  .ab-off__row { grid-template-columns: 148px minmax(0, 1fr) 380px; }
  .ab-off__det { grid-column: 3; grid-row: 1; }
}
.ab-off__body .link-arrow { margin-top: var(--s-4); }
.ab-off__contact { margin-top: var(--s-3); }

/* the closing plate: full-bleed, decorative, unframed, uncaptioned. A DIRECT
   child of the <section>, never a negative-margin bleed out of .container:
   `margin-inline: calc(50% - 50vw)` overflows by the scrollbar width. */
.ab-off__plate {
  position: relative;
  margin-top: var(--s-12);
  height: 260px;
  overflow: clip;
}
@media (min-width: 768px)  { .ab-off__plate { height: 320px; } }
@media (min-width: 1024px) { .ab-off__plate { height: 380px; } }
.ab-off__plate picture { position: absolute; inset: 0; display: block; }
.ab-off__plate img { width: 100%; height: 100%; object-fit: cover; object-position: var(--po, 50% 44%); }

/* =========================================================================
   CONTACT  (SPEC-P-R2 §4)
   The page now adopts the HOMEPAGE's own contact console and office plate,
   verbatim: .ct__console, .ct__rail, .ct__photo, .off__tabs, .off__ind and
   .off__plate all live in next.css and are consumed here, not duplicated.
   SPEC-08-R2 §12's "scope these so the contact page is not caught by them" is
   deliberately reversed by SPEC-P-R2 §4.4 and by orchestrator ruling 4: the
   scoping stays, and this template now emits those wrappers on purpose.

   Nothing inside the <form> changes. .ct__top, .ct__intro, .ph-rail and the
   welcome band are gone from the template; the two rules below are the only
   contact-specific CSS this file still owns.
   ========================================================================= */
/* the appointments link in the console's navy rail. /appointments/ is in
   neither the primary nav nor the footer. */
.ct__book {
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid rgba(255, 255, 255, .16);
  width: 100%;
}
.ct__console .ct__book { color: #fff; }
.ct__console .ct__book:hover { color: var(--gold-200); }
/* the hours line sits above the book link inside the rail, so its own top rule
   would be the second hairline in 24px */
.ct__console .ct__direct-hours + .ct__book { border-top: 0; padding-top: 0; }

/* =========================================================================
   APPOINTMENTS  (SPEC-P2 Part B)
   Calendly rendered as an <iframe> we write ourselves. SPEC-00 §8.4 forbids
   third-party SCRIPT in this document; a frame executes in its own origin
   and is the precedent SPEC-08 §9.2 already set with the two maps. The only
   thing lost is auto-height, which degrades to an inner scrollbar and never
   to hidden content.
   ========================================================================= */
.bk { position: relative; isolation: isolate; overflow: clip; background: var(--wash); padding-block: var(--section-y); }

/* --- the booking console (SPEC-P-R2 §5.3) -------------------------------
   ONE card, the same object as the homepage's contact console, MIRRORED:
   calendar left, navy rail right. A reader arriving from /contact-us/ should
   recognise the object and see at once that its content is not the same.
   Below 1024 the rail comes FIRST: Calendly's own frame is ~1,000px tall on a
   phone and scrolls internally, so three short rows and a phone number above
   it cost ~380px and buy the reader the agenda before a scroll they cannot
   easily reverse. Above 1024 the calendar leads, because it is the action. */
.bk__console {
  display: grid;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-3);            /* the section's ONE elevated object */
  overflow: clip;
}

/* --- the embed shell: bled to the card edge, no border and no radius of its
       own; the card's overflow does the clipping. --- */
.bk__embed {
  position: relative;
  isolation: isolate;
  order: 2;
  overflow: hidden;
  min-height: 1000px;                 /* ≤767 */
  background: var(--navy-050);
}
@media (min-width: 768px)  { .bk__embed { min-height: 900px; } }
@media (min-width: 1024px) { .bk__embed { order: 0; min-height: clamp(760px, 88vh, 900px); } }
/* below 1024 the rail and the calendar stack; give the calendar its own card
   with air above it instead of butting against the navy (owner, 2026-09-20) */
@media (max-width: 1023px) {
  .bk__console { background: transparent; border: 0; box-shadow: none; overflow: visible; row-gap: var(--s-6); }
  .bk__rail  { border-radius: var(--r-lg); }
  .bk__embed { border-radius: var(--r-lg); border: 1px solid var(--line); box-shadow: var(--sh-3); }
}

.bk__frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; z-index: var(--z-media); }

/* the plate sits BEHIND the frame, so it is the loading state and the failure
   state at once and is never a white hole. The functional escape hatch lives
   outside the frame's box (.bk__out), never underneath it: a blocked-but-
   present iframe still consumes pointer events over its own area. */
.bk__fallback {
  position: absolute;
  inset: 0;
  z-index: var(--z-pattern);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: var(--s-6);
  text-align: center;
  background: var(--navy-050);
  color: var(--navy);
}
.bk__cal-glyph { width: 28px; height: 28px; margin-bottom: var(--s-2); }
.bk__fb-title { font: var(--t-label); text-transform: uppercase; letter-spacing: .06em; }
.bk__fb-note  { font: var(--t-cap); color: var(--ink-2); max-width: 34ch; }

/* --- the navy rail (SPEC-P-R2 §5.3) --------------------------------------
   Flat navy, no pattern and no gradient, SPEC-08-R2 §5's rule for the contact
   rail and for the same reason. */
.bk__rail {
  order: 0;
  display: flex;
  flex-direction: column;
  padding: var(--s-6);
  background: var(--navy);
  color: #fff;
}
@media (min-width: 768px)  { .bk__rail { padding: var(--s-7); } }
@media (min-width: 1024px) { .bk__rail { order: 1; padding: var(--s-9); } }
.bk__rail .eyebrow { color: rgba(255, 255, 255, .86); }
.bk__rail .eyebrow .tick { width: 28px; }   /* the rail's ONE gold instance */

/* the three ledger rows, on navy. Same geometry as the light ledger; the two
   colour pairs are the only difference (SPEC-P-R2 §2.4). */
.bk__list { margin-top: var(--s-7); border-top: 1px solid rgba(255, 255, 255, .16); }
.bk__item {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  column-gap: var(--s-4);
  padding-block: var(--s-6);
  border-bottom: 1px solid rgba(255, 255, 255, .16);
}
.bk__item-num {
  grid-column: 1; grid-row: 1;
  font: 600 30px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.03em;
  color: rgba(255, 255, 255, .18);     /* aria-hidden, never the only carrier */
}
.bk__item-title {
  grid-column: 2; grid-row: 1;
  align-self: center;
  font: 600 20px/1.26 var(--font-display);
  letter-spacing: -.014em;
  color: #fff;
}
.bk__item-text {
  grid-column: 1 / -1; grid-row: 2;
  margin-top: 10px;
  font: var(--t-body-sm);
  color: rgba(255, 255, 255, .72);     /* ~8.4:1 */
  max-width: 34ch;
}
@media (min-width: 768px) {
  .bk__item { grid-template-columns: 64px minmax(0, 1fr); column-gap: var(--s-5); }
  .bk__item-num { font-size: 34px; }
  .bk__item-text { grid-column: 2; }
}
@media (min-width: 1024px) { .bk__item-num { font-size: 38px; } }
@media (min-width: 1180px) { .bk__item-num { font-size: 44px; } }

/* the direct-dial block, recoloured for the rail */
.bk__rail .ct__direct {
  margin-top: var(--s-7);
  padding: 0;
  padding-top: var(--s-7);
  border-left: 0;
  border-top: 0;   /* the list's last row already draws the rule; two rules read as a bar (owner, 2026-09-20) */
}
.bk__rail .ct__direct-label { color: #fff; }
.bk__rail .ct__tel  { color: #fff; }
.bk__rail .ct__mail { color: #fff; }
.bk__rail .ct__tel:hover,
.bk__rail .ct__mail:hover { color: var(--gold-200); }
.bk__rail .ct__direct-hours { color: var(--navy-100); }

/* --- the always-visible escape hatch -------------------------------------
   In normal flow OUTSIDE the card, never underneath the frame: a blocked-but-
   present iframe still consumes pointer events over its own area, so a link
   painted under it would be visible in some failure modes and unclickable in
   others. It now reads as the card's footnote. */
.bk__out {
  margin-top: var(--s-8);
  padding: var(--s-6);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
}
.bk__out-title { font: var(--t-label); color: var(--ink); }
.bk__out-body  { margin-top: var(--s-2); font: var(--t-body-sm); color: var(--ink-3); max-width: 54ch; }
.bk__out .link-arrow { margin-top: var(--s-4); }
@media (min-width: 768px) {
  .bk__out { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-7); }
  .bk__out-copy { flex: 1 1 auto; }
  .bk__out .link-arrow { flex: 0 0 auto; margin-top: 0; }
}

@media (min-width: 1024px) {
  .bk__console { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); border-radius: var(--r-xl); }
}

/* =========================================================================
   THANK-YOU  (SPEC-P2 Part C)
   No CTA band and no primary button anywhere: the reader converted 400ms
   ago. No time word appears on the page except the office hours.
   ========================================================================= */
/* the mark inverts: a gold disc carrying a navy check. On the navy plate the
   old navy-disc-with-gold-check is a disc that is not there. 9.2:1 for the
   glyph against its own disc, and a legitimate SPEC-00 §3.5 use. */
.ty__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px; height: 56px;
  margin-bottom: var(--s-5);
  border-radius: var(--r-pill);
  background: var(--gold);
  color: var(--navy);
}
.ty__mark svg { width: 26px; height: 26px; }

.ty-next { background: var(--paper); padding-block: var(--section-y); }
.ty-next__h, .ty-read__h { font: var(--t-d3); letter-spacing: -.016em; color: var(--ink); }

.ty-next__grid { display: grid; gap: var(--s-8); }
@media (min-width: 1024px) {
  .ty-next__grid { grid-template-columns: minmax(0, 1fr) 380px; column-gap: var(--s-11); align-items: start; }
  /* the shared ledger zeroes its top margin because on a service page the
     list sits BESIDE its aside. Here it sits UNDER its own h2. */
  .ty-next__main .svc-del__list { margin-top: var(--s-8); }
}

/* the phone number is inline body text, not a button: it is a fallback, not an ask */
.ty-next__call   { margin-top: var(--s-8); font: var(--t-body-sm); color: var(--ink-3); }
.ty-next__call a { color: var(--navy); font-weight: 600; text-decoration: none; }
.ty-next__call a:hover { color: var(--navy-500); text-decoration: underline; text-underline-offset: 4px; }

/* the surviving photograph: a column plate in the band's right track, so it
   stops fighting the copy for the page's centre line (defect 14) */
.ty-next__plate {
  position: relative;
  margin: var(--s-8) 0 0;
  aspect-ratio: 3 / 2;               /* ≤767 */
  overflow: clip;
  border-radius: var(--r-sm);
  background: var(--navy-050);
}
.ty-next__plate picture { position: absolute; inset: 0; display: block; }
.ty-next__plate img { width: 100%; height: 100%; object-fit: cover; object-position: var(--po, 50% 44%); }
@media (min-width: 768px)  { .ty-next__plate { aspect-ratio: 16 / 9; margin-top: var(--s-9); border-radius: var(--r-md); } }
@media (min-width: 1024px) { .ty-next__plate { aspect-ratio: 4 / 5; margin-top: 0; } }

.ty-read { background: var(--wash); padding-block: var(--section-y); }

/* =========================================================================
   404  (SPEC-P2 Part D)
   No primary button: a gold CTA on an error page is asking the reader to buy
   something immediately after failing them. No search box, no giant numeral,
   no mascot, no auto-redirect.
   ========================================================================= */
.e4 { background: var(--paper); padding-block: var(--section-y); }
.e4__h { font: var(--t-d3); letter-spacing: -.016em; color: var(--ink); }
.e4__measure { max-width: 780px; margin-inline: auto; }

/* --- the ledger LINK row, shared by 404 band 2 and thank-you band 3 -------
   One grammar for both: a ruled row, the name in a fixed left track, the
   description in the middle, the arrow right. Hover and :focus-within paint a
   --navy-050 fill, a 4px inset gold left edge and a 4px arrow travel. NEVER a
   lift, never a shadow, never a border-colour change; every row is therefore
   the same height and the list can never carry an orphan cell. The whole row
   is one <a>, so the target clears 44px from padding alone. */
.pl-rows { margin-top: var(--s-7); border-top: 1px solid var(--line); }
.pl-rows > li { border-bottom: 1px solid var(--line); }
.pl-row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 40px;
  align-items: center;
  gap: 2px var(--s-5);
  min-height: 72px;
  padding: var(--s-6) var(--s-4) var(--s-6) var(--s-4);
  text-decoration: none;
  transition: background-color var(--dur-1) var(--ease-out),
              box-shadow var(--dur-1) var(--ease-out);
}
.pl-row__name { grid-column: 1; font: var(--t-d4); color: var(--navy); transition: color var(--dur-1) var(--ease-out); }
.pl-row__desc { grid-column: 1; font: var(--t-body-sm); color: var(--ink-3); }
.pl-row__go   { grid-column: 2; grid-row: 1 / -1; justify-self: end; color: var(--navy); }
.pl-row__go svg { display: block; width: 16px; height: 16px; transition: transform var(--dur-1) var(--ease-out); }
.pl-row:hover,
.pl-row:focus-within { background: var(--navy-050); box-shadow: inset 4px 0 0 var(--gold); }
.pl-row:hover .pl-row__name { color: var(--navy-500); }
.pl-row:hover .pl-row__go svg { transform: translateX(4px); }
@media (min-width: 768px) {
  .pl-row {
    grid-template-columns: 260px minmax(0, 1fr) 40px;
    align-items: baseline;
    padding-block: var(--s-6);
  }
  .pl-row__name { grid-column: 1; }
  .pl-row__desc { grid-column: 2; }
  .pl-row__go   { grid-column: 3; grid-row: 1; align-self: center; }
}

/* the one photograph on the 404, held to the list's own 780px measure and
   BELOW it, unframed and uncaptioned. SPEC-P-R2 §7.2 deleted it outright;
   orchestrator ruling 1 keeps it at the restore path that section itself
   specifies. */
.e4__plate {
  position: relative;
  margin-top: var(--s-10);
  aspect-ratio: 16 / 9;                /* ≤767 */
  overflow: clip;
  border-radius: var(--r-sm);
  background: var(--navy-050);
}
.e4__plate picture { position: absolute; inset: 0; display: block; }
.e4__plate img { width: 100%; height: 100%; object-fit: cover; object-position: var(--po, 50% 44%); }
@media (min-width: 768px) { .e4__plate { aspect-ratio: 16 / 6; border-radius: var(--r-md); } }

/* =========================================================================
   LEGAL DOCUMENTS  (SPEC-P3 Part A)
   Typography applied to arbitrary CMS output. Every rule is an element
   selector inside one scope and must degrade gracefully against markup it
   did not author: a table, a definition list, a blockquote, a list five deep.
   The legal text itself is rendered unmodified.
   ========================================================================= */
.lg-doc { background: var(--paper); padding-block: var(--s-10) var(--section-y); }

/* SPEC-P-R2 §8.1 item 2 | the title and the document share ONE spine. The
   article is already centred; the h1 above it was not, so the page had two
   left edges 342px apart. The hero's copy block is centred on the article's
   own measure, so the title, the effective-date line and the first paragraph
   all begin at the same x.

   .lg-doc__grid IS the section's .container, so its 72ch cap is a BORDER box
   and the article begins one --gutter inside it. .ihero__copy is inside a
   .container
   too, so capping it at the same 72ch puts its content on the same x.
   Measured at 1440: both at 398.9px. Do not add the gutters back here. */
.page-legal .ihero__copy { max-width: 72ch; margin-inline: auto; }

.lg-doc__grid { display: grid; gap: var(--s-8); }
/* SPEC-P3 §A3.2 asks for "66ch ... at 17px it measures ~612px", inside
   SPEC-00 §1.3's 78ch ceiling and inside the 45-75 character band. Measured,
   1ch of Mulish at 17px is 10.2px, so 66ch renders 673px and sets ~79
   characters to the line: at the ceiling rather than comfortably inside it.
   The spec's stated MEASURE is the binding number, not its ch arithmetic,
   and 60ch resolves to exactly the 612px it asked for. */
.lg-doc__grid > .legal__body { max-width: 60ch; }        /* = 612px at 17px Mulish */

/* single column, centred — the state of all three documents today */
@media (min-width: 768px) { .lg-doc__grid { justify-content: center; } }

/* SPEC-P-R2 §8.1 | the two-state grid. :has() needs no fallback: without
   support the grid stays one column and the article stays at 60ch, which is
   the current rendering. The 72ch is on the TRACK, so the measure never grows
   past the article's own 60ch and the extra 12ch is optical breathing room. */
@media (min-width: 1024px) {
  /* no TOC: the state all three documents are in today */
  .lg-doc__grid:not(:has(.lg-toc)) {
    grid-template-columns: minmax(0, 1fr);
    max-width: 72ch;
    margin-inline: auto;
  }
  /* six or more real <h2>: the sticky rail returns beside the article */
  .lg-doc__grid:has(.lg-toc) {
    grid-template-columns: 260px minmax(0, 1fr);
    column-gap: var(--s-11);
    max-width: none;
    align-items: start;
    justify-content: start;
  }
}

/* --- base typography ---
   17px, not --t-body's 16: these are the longest continuous text blocks on
   the site and the only pages with no images and no visual pacing.
   --ink-2 (8.3:1), not --ink-3: read at length, often on a phone, often by
   someone with a reason to be careful. */
.legal__body {
  font: 400 17px/1.75 var(--font-ui);
  color: var(--ink-2);
  text-wrap: pretty;
}
.legal__body > * + * { margin-top: var(--s-5); }
.legal__body p { max-width: 60ch; }      /* same 612px measure, see above */

.legal__body a {
  color: var(--navy);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  overflow-wrap: anywhere;           /* a bare URL in a legal doc must not overflow */
  transition: color var(--dur-1) var(--ease-out);
}
.legal__body a:hover { color: var(--navy-500); text-decoration-thickness: 2px; }

.legal__body strong { font-weight: 700; color: var(--ink); }
/* SPEC-00 §1.3 bans italics and no italic face is loaded, so a browser would
   synthesise an oblique from the roman. Emphasis is preserved as Mulish 600,
   which is already in the stylesheet. Scoped to this body only. */
.legal__body :is(em, i) { font-style: normal; font-weight: 600; color: var(--ink); }

/* --- real headings, if and when the content has them --- */
.legal__body h2 {
  margin-top: var(--s-10);
  padding-top: var(--s-5);
  border-top: 1px solid var(--line);
  font: var(--t-d3);
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -.016em;
  line-height: 1.26;
  color: var(--ink);
  scroll-margin-top: calc(var(--header-h) + var(--s-6));
  text-wrap: balance;
}
.legal__body h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.legal__body h3 {
  margin-top: var(--s-8);
  font: var(--t-d4);
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--navy);
}
.legal__body h4 { margin-top: var(--s-6); font: 600 16px/1.4 var(--font-ui); color: var(--navy); }
.legal__body :is(h2, h3, h4) + * { margin-top: var(--s-4); }

/* --- the ol > li > strong pseudo-heading, which is what actually ships ---
   An <ol> whose only child is an <li> containing only <strong> is a section
   heading typed as a list. It is styled as the heading it is, WITHOUT
   touching the markup, and deliberately one step lighter than a real <h2>:
   it is not a heading in the document outline and no amount of CSS makes it
   one. The demotion is a visible tell that the underlying markup is wrong.
   Without :has() these render as ordinary numbered lists with bold text —
   the current live appearance, and no worse. */
.legal__body ol > li > strong:only-child {
  display: block;
  font: var(--t-d4);
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -.010em;
  color: var(--ink);
}
.legal__body ol:has(> li:only-child > strong:only-child) {
  margin-top: var(--s-9);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line-2);
  list-style-position: outside;
  padding-left: var(--s-6);
}
.legal__body ol:has(> li:only-child > strong:only-child) > li::marker {
  font: 600 15px/1 var(--font-display);
  color: var(--navy-300);
}
.legal__body ol:has(> li:only-child > strong:only-child) + * { margin-top: var(--s-4); }

/* --- lists ---
   next.css resets every ul/ol to list-style:none, so the markers are put
   back here. The custom <ul> bullet is a 5px navy-300 dot: SPEC-00 §3.5 does
   not list list-markers among gold's permitted uses, and 54 gold dots on the
   privacy policy would be the largest gold violation available on this site.
   li + li gets 12px, not 20: legal lists run ten items long and 20px turns a
   definitions list into three screens. */
.legal__body ol { list-style: decimal outside; padding-left: var(--s-6); }
.legal__body ul { list-style: none; padding-left: var(--s-5); }
.legal__body ul > li { position: relative; padding-left: var(--s-5); }
.legal__body ul > li::before {
  content: "";
  position: absolute;
  left: 0; top: .68em;
  width: 5px; height: 5px;
  border-radius: var(--r-pill);
  background: var(--navy-300);
}
.legal__body ol > li::marker { color: var(--navy-300); font-variant-numeric: tabular-nums; }
.legal__body li + li { margin-top: var(--s-3); }
.legal__body li > :is(ul, ol) { margin-top: var(--s-3); }
.legal__body li p { margin-top: var(--s-2); }

/* --- the effective-date line ---
   A positional assumption, stated as one. All three documents open with
   their effective date. If a future document opens on body copy instead, the
   paragraph renders one size smaller with a rule under it, which reads as a
   standfirst. The rule was chosen for exactly that property. No PHP tries to
   detect a date: a regular expression that scans a legal document for a date
   and reformats it can silently misstate a legal fact.

   SPEC-P-R2 §8.1 item 3 turns it into the document's MASTHEAD, and that
   treatment is scoped to .page-legal: templates/post-next.php,
   case-study-next.php and service-next.php all render .legal__body too, and on
   a blog post the first paragraph is a standfirst, not a metadata line. */
.legal__body > p:first-of-type {
  margin-bottom: var(--s-7);
  padding-bottom: var(--s-5);
  border-bottom: 1px solid var(--line);
  font: var(--t-body-sm);
  color: var(--ink-3);
  max-width: none;
}
.legal__body > p:first-of-type strong { color: var(--navy); font-weight: 700; }

/* the masthead: the same 72x3 gold mark every SPEC-04-R2 panel carries,
   sitting on the left end of its own rule. It is the only ornament the page
   gets, and it turns a stray fragment above a hairline into the one piece of
   metadata a legal document is supposed to lead with. Nothing computes,
   formats or defaults the date; the selector styles whatever the first
   paragraph is. */
.page-legal .legal__body > p:first-of-type {
  position: relative;
  margin-bottom: var(--s-8);
  font: var(--t-label);
  letter-spacing: .01em;
  color: var(--ink-2);
}
.page-legal .legal__body > p:first-of-type::after {
  content: "";
  position: absolute;
  left: 0; bottom: -1px;
  width: 56px; height: 3px;
  background: var(--gold);
}
@media (min-width: 1024px) { .page-legal .legal__body > p:first-of-type::after { width: 72px; } }

/* --- tables. None exists today; a legal document is exactly the kind of
   content that acquires one, and the day it does the page must not break.
   Horizontal scroll is contained by the wrapper, never inherited by the page. */
.legal-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.legal__body table {
  width: 100%;
  min-width: 480px;
  border-collapse: collapse;
  font: 400 15px/1.55 var(--font-ui);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
}
.legal__body thead th {
  text-align: left;
  vertical-align: bottom;
  padding: var(--s-3) var(--s-4);
  font: var(--t-label);
  letter-spacing: .02em;
  color: var(--navy);
  background: var(--navy-050);
  border-bottom: 1px solid var(--line);
}
.legal__body :is(td, tbody th) {
  padding: var(--s-3) var(--s-4);
  vertical-align: top;
  border-top: 1px solid var(--line-2);
  color: var(--ink-2);
}
.legal__body tbody th { text-align: left; font-weight: 600; color: var(--ink); }
.legal__body tbody tr:first-child :is(td, th) { border-top: 0; }
.legal__body caption { caption-side: bottom; padding-top: var(--s-3); font: var(--t-cap); color: var(--ink-3); text-align: left; }

/* --- everything else the CMS might emit --- */
.legal__body blockquote { margin-left: 0; padding-left: var(--s-5); border-left: 3px solid var(--navy-100); color: var(--ink-2); }
.legal__body :is(img, video) { max-width: 100%; height: auto; border-radius: var(--r-sm); }
.legal__body hr { margin-block: var(--s-9); border: 0; border-top: 1px solid var(--line); }
.legal__body :is(code, pre) { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9em; }
.legal__body pre { overflow-x: auto; padding: var(--s-4); background: var(--wash); border: 1px solid var(--line); border-radius: var(--r-sm); }
.legal__body dt { font-weight: 700; color: var(--ink); margin-top: var(--s-4); }
.legal__body dd { margin-left: var(--s-5); }
.legal__body :is(h5, h6) { margin-top: var(--s-5); font: 600 15px/1.4 var(--font-ui); color: var(--navy); }

/* --- the table of contents (SPEC-P3 §A4.3) ---
   Renders only when the document has six or more real <h2>s. Today all three
   have zero, so no TOC renders and the page is single-column and centred.
   The sticky rail is this page's one sticky element and its travel is ten
   times SPEC-00 §6.4's 240px floor. No scroll-spy: SPEC-00 §8.4 enumerates
   the permitted JavaScript exhaustively and this is not among them. */
.lg-toc { margin-bottom: var(--s-8); }
@media (min-width: 1024px) {
  .lg-toc { margin-bottom: 0; }
  .lg-toc__in {
    position: sticky;
    top: calc(var(--header-h) + var(--s-6));
    z-index: var(--z-rail);
    max-height: calc(100vh - var(--header-h) - var(--s-10));
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}
.lg-toc__h {
  font: var(--t-eyebrow);
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-3);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line);
}
.lg-toc__list { margin-top: var(--s-4); counter-reset: toc; list-style: none; }
.lg-toc__list li { counter-increment: toc; }
.lg-toc__list li + li { margin-top: var(--s-1); }
.lg-toc__list a {
  display: flex;
  gap: var(--s-3);
  align-items: baseline;
  min-height: 44px;
  padding: var(--s-2) 0;
  font: var(--t-body-sm);
  color: var(--ink-2);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease-out);
}
.lg-toc__list a::before {
  content: counter(toc, decimal-leading-zero);
  flex: 0 0 auto;
  width: 22px;
  font: 600 12px/1.4 var(--font-display);
  font-variant-numeric: tabular-nums;
  color: var(--navy-300);
}
.lg-toc__list a:hover { color: var(--navy); }
.lg-toc__list a:hover::before { color: var(--navy); }

/* =========================================================================
   SITEMAP  (SPEC-P3 Part B)
   No cards: seven bordered boxes containing nothing but links is a page of
   frames. On --wash the flat groups read as a document, which is what a
   sitemap is. The 2px rule under each <h2> is doing all the grouping work.
   ========================================================================= */
.sm { background: var(--wash); padding-block: var(--section-y); }

.sm__grid { display: grid; gap: var(--s-8); }
@media (min-width: 561px)  { .sm__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-9) var(--s-8); } }
@media (min-width: 1024px) {
  .sm__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-10) var(--s-8); }
  .sm__group--wide { grid-column: span 2; }
}
/* SPEC-P3 §B3 sized the groups against ~15 services. The site publishes 100.
   A single 100-item column is 4,800px of one list, so the Services group
   takes the full row and sets its list in columns. Flagged in §7 item 7. */
.sm__group--full { grid-column: 1 / -1; }

/* SPEC-P-R2 §9.1 items 2 and 3. The heading row is a baseline-aligned flex
   row carrying a live count of what the group just rendered: a description,
   not a claim, and it cannot disagree with the list beneath it. The rule goes
   2px --navy-100 to 1px --line and the heading --ink to --navy; a 2px rule
   under seven headings on one page is seven heavy horizontals, and 1px plus a
   navy heading groups just as hard without the page reading as a form. */
.sm__h {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-5);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line);
  font: var(--t-d4);
  color: var(--navy);
}
.sm__count {
  flex: 0 0 auto;
  font: var(--t-cap);
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
}
.sm__list { margin-top: var(--s-4); }
.sm__list li + li { margin-top: var(--s-1); }
.sm__list a {
  display: block;
  min-height: 44px;
  padding: var(--s-2) 0;
  font: var(--t-body-sm);
  color: var(--ink-2);
  text-decoration: none;
  /* a transition on padding-left, not on transform: ninety anchors must not
     each get their own compositing layer */
  transition: color var(--dur-1) var(--ease-out), padding-left var(--dur-1) var(--ease-out);
}
.sm__list a:hover {
  color: var(--navy);
  padding-left: var(--s-2);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1.5px;
}
.sm__list--2 li, .sm__list--3 li { break-inside: avoid; }
@media (min-width: 768px)  { .sm__list--2 { columns: 2; column-gap: var(--s-8); }
                             .sm__list--3 { columns: 2; column-gap: var(--s-8); } }
@media (min-width: 1024px) { .sm__list--3 { columns: 3; } }
.sm__group .link-arrow { margin-top: var(--s-4); }

/* =========================================================================
   SERVICE SINGLE  (SPEC-S1)
   Eight bands and four different list grammars. The failure mode of this page
   is not scarcity, it is sameness: strip it down and the content is four
   repeaters that are structurally identical, so rendered naively a service
   page is four consecutive lists that look the same and the reader stops at
   the second. Deliverables get a definition grid, process gets a drawn rail,
   FAQ gets native <details>, and the Approach repeater is not rendered at all.

   Appended here rather than shipped as assets/svc.css: pages.css is ALREADY
   fetched by this template for .ihero, .e4__link and .legal__body, so the
   real trade is a fourth render-blocking request on 100 URLs against ~9 KB
   inside a file the page downloads anyway. SPEC-S2 §13.4 closed it that way.
   ========================================================================= */

/* --- 1. hero -------------------------------------------------------------
   DELETED. .svchero, its seam, its plane and its glass rail are replaced by
   .ihero .ihero--single (SPEC-IH-R3 §12.1). The only survivor is the navy
   band's focus ring, which MOVED to next.css with the rest of the closer
   (the homepage loads next.css only). The .svchero copy of this rule is
   deleted with the field (SPEC-IH-R3 §10.5). */

/* --- 2. overview: DELETED (SPEC-SVC-R4 §4.3) ----------------------------
   .svc-open* is superseded by .s4-open*, which turns the band's right track
   into the page's one reading rail. service-next.php was its only consumer,
   so the rules are removed rather than left on disk to be picked up again.
   .svc-more below SURVIVES: industry-next.php renders it too. */

/* --- 2b. the disclosure (SPEC-SI-R3 §4.2.5) ------------------------------
   Prose is not capped by characters. It is capped by PARAGRAPH COUNT, once, at
   a number no page on the site reaches: 1-8 open, 9..n here. Native <details>,
   zero JavaScript, fully in the DOM so it is indexed and printable, and the
   answer to "what if somebody pastes 40 paragraphs" is therefore a disclosure
   and never an ellipsis. On the 100 services measured today it never renders.
   It is also the industry Opening band's overflow answer, at the same
   threshold, so the two templates behave identically. */
.svc-more { margin-top: var(--s-6); border-top: 1px solid var(--line-2); }
.svc-more__sum {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4);
  min-height: 48px;
  font: 600 15px/1.4 var(--font-ui);
  color: var(--navy);
  cursor: pointer;
  list-style: none;
}
.svc-more__sum::-webkit-details-marker { display: none; }
.svc-more__sum:focus-visible { outline: 2px solid var(--navy); outline-offset: 3px; border-radius: var(--r-xs); }
.svc-more__chev {
  flex: 0 0 auto;
  width: 16px; height: 16px;
  color: var(--navy-300);
  transition: transform var(--dur-2) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.svc-more[open] .svc-more__chev { transform: rotate(180deg); color: var(--navy); }
.svc-more__body { padding-bottom: var(--s-5); }
.svc-more__body > :first-child { margin-top: var(--s-5); }
@media print {
  .svc-more { display: block; }
  .svc-more__body { display: block !important; }
}

/* --- 3. deliverables (§5.3) --------------------------------------------- */
.svc-del {
  position: relative; isolation: isolate; overflow: clip;
  padding-block: var(--section-y);
  background: var(--wash);
}
/* .svc-del__pattern is DELETED, not left on disk. SPEC-S1-R2 stopped emitting
   the dot field on the service template (one device per band) and SPEC-I-R2
   §A6 stops it on the industry template: its own comment here documented a
   .311 mask alpha over the top-right item title at 1024, and it is invisible
   at the widths where it did not fight the copy. Zero consumers, so the rule
   and its media query go with them. */

.svc-del .container { position: relative; z-index: var(--z-content); }
.svc-del__list { display: grid; gap: var(--s-7); margin-top: var(--s-9); }
@media (min-width: 768px)  { .svc-del__list { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--s-8); } }
@media (min-width: 1024px) { .svc-del__list { column-gap: var(--s-10); row-gap: var(--s-8); margin-top: var(--s-10); } }

/* --navy-100 and not --line: on --wash a --line hairline all but vanishes,
   and the rule is the only structure the band has. 1px non-text UI, 3.1:1. */
.svc-del__item { padding-top: var(--s-5); border-top: 1px solid var(--navy-100); }
.svc-del__num {
  display: block;
  font: 600 12px/1 var(--font-display);
  font-variant-numeric: tabular-nums; letter-spacing: .08em;
  color: var(--ink-3);                   /* not --navy-300: 3.2:1 on --wash fails AA at 12px */
  margin-bottom: var(--s-3);
}
.svc-del__title { font: var(--t-d4); letter-spacing: -.010em; color: var(--navy); overflow-wrap: anywhere; }
.svc-del__text  { margin-top: var(--s-2); }

/* --- 3b. the round-two deliverables band (SPEC-S1-R2 §6) -----------------
   Everything above this comment is the SPEC-S1 recipe. It stays because it is
   still the fallback shape for .svc-del anywhere the round-two scope does not
   reach; the composition below is scoped to the two page classes that do use
   it, so nothing else on the site moves.

   SPEC-I-R2 §A6 put industry-next.php's pains band on this same geometry
   rather than on a second copy of it: the two bands are the same object - a
   numbered list of six to ten short titled entries - and a parallel component
   under an ind- prefix is what SPEC-00 §10 forbids. Two deltas are scoped to
   .page-industry further down the file: no full-bleed --paper panel, because
   that band already changes ground, and the 72x3 gold mark therefore sits on
   the list's own top rule.

   One continuous column of ruled ledger rows inside a full-bleed --paper
   panel: the two-column grid is what made ten deliverables read as two
   unrelated lists, and the dot field is not emitted on either template any
   more (one device per band). No hover state anywhere in here: these rows are
   not links and a fill on a non-interactive row is a false affordance. */
:is(.page-service, .page-case-study) .svc-del { overflow: clip; padding-block: 0; }
:is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del--paper { background: var(--paper); }
:is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del--wash  { background: var(--wash); }

:is(.page-service, .page-case-study) .svc-del__panel {
  position: relative;
  padding-block: var(--s-10);
  background: var(--paper);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
/* the gold mark sits ON the panel's top rule, aligned to the container's own
   left text edge. It is the band's single gold appearance. */
:is(.page-service, .page-case-study) .svc-del__panel::before {
  content: "";
  position: absolute;
  top: -1px;
  left: max(var(--gutter), calc(50% - (var(--container) / 2) + var(--gutter)));
  width: 56px; height: 3px;
  background: var(--gold);
}

:is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__grid { display: grid; }
:is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__aside { max-width: none; }
:is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__list {
  --row-pad: 30px;
  display: block;
  margin-top: var(--s-9);
  border-top: 1px solid var(--line);
}
:is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__item {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  column-gap: var(--s-4);
  padding-top: 0;
  padding-block: var(--row-pad);
  border-top: 0;
  border-bottom: 1px solid var(--line);
}
:is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__num {
  grid-column: 1; grid-row: 1;
  margin-bottom: 0;
  font: 600 30px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.03em;
  color: rgba(2, 45, 90, .16);           /* aria-hidden, never the only carrier */
}
:is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__title {
  grid-column: 2; grid-row: 1;
  align-self: center;
  font: 600 19px/1.26 var(--font-display);
  letter-spacing: -.014em;
}
:is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__text {
  grid-column: 1 / -1; grid-row: 2;
  margin-top: 10px;
  color: var(--ink-2);
  max-width: 38ch;
}
@media (min-width: 768px) {
  :is(.page-service, .page-case-study) .svc-del__panel { padding-block: var(--s-10); }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__list { margin-top: var(--s-9); }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__item { grid-template-columns: 64px minmax(0, 1fr); column-gap: var(--s-5); }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__num { font-size: 34px; }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__title { font-size: 21px; }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__text { grid-column: 2; }
}
@media (min-width: 1024px) {
  :is(.page-service, .page-case-study) .svc-del__panel { padding-block: var(--s-11); }
  :is(.page-service, .page-case-study) .svc-del__panel::before { width: 72px; }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__grid { grid-template-columns: 320px minmax(0, 1fr); column-gap: var(--s-10); }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__list { margin-top: 0; }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__item {
    grid-template-columns: 76px minmax(0, 1fr) 260px;
    column-gap: var(--s-5);
  }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__num { font-size: 38px; }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__title { font-size: 22px; }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__text { grid-column: 3; grid-row: 1; margin-top: 0; align-self: center; }
}
@media (min-width: 1180px) {
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__grid { grid-template-columns: 380px minmax(0, 1fr); column-gap: var(--s-11); }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__item { grid-template-columns: 96px minmax(0, 1fr) 300px; column-gap: var(--s-6); }
  :is(.page-service, .page-industry, .page-about, .page-thank-you, .page-case-study) .svc-del__num { font-size: 44px; }
}

/* --- 3c. THE REGISTER GRID (SPEC-SI-R3-AMEND §4) ------------------------
   The band the client rejected, rebuilt. Everything above stays on disk and
   keeps rendering: industry-next.php's "what we do" ledger is gone from
   .svc-del__* but page-about.php, page-thank-you.php and case-study-next.php
   are not, so object 1 survives in the shared vocabulary and this is object 6
   rather than a replacement. SPEC-00 §10's ban is not breached, because a
   parallel component is two selectors doing the same job ON THE SAME PAGE and
   after this change no page renders both a .svc-del__item and a .svc-cov__cell.

   The gap is ZERO because the dividers ARE the gap: separation comes from
   --cell-x inside each cell, and the list's negative margin-inline pulls the
   outer cells' padding back out to the container text edge, so the first
   column's copy aligns with the h2 above it. .svc-del and .svc-del__panel both
   carry overflow: clip and the negative margin is never larger than the
   gutter, so this can never produce a horizontal scrollbar.

   No lift, no shadow, no scale on hover. That is card language and removing it
   is the point. */
.svc-cov__head { max-width: none; }
.svc-cov__count {
  margin-top: var(--s-3);
  font: 600 13px/1.4 var(--font-ui);
  letter-spacing: .02em;
  color: var(--ink-3);
}

.svc-cov__list {
  --cell-x: 0px;
  --cell-bleed: 16px;                    /* the link fill's inline overshoot */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  margin-top: var(--s-7);
  margin-inline: calc(var(--cell-x) * -1);
  border-bottom: 1px solid var(--line);
}
.svc-cov__cell,
.svc-cov__link {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-content: start;
}
.svc-cov__cell {
  position: relative;
  padding: 22px var(--cell-x) 26px;
  border-top: 1px solid var(--line);
}
.svc-cov__cell > .svc-cov__link {
  position: relative;
  isolation: isolate;
  grid-column: 1 / -1;
  text-decoration: none;
}
/* a cell with no `url` is not a link and has no state of any kind: a hover on
   a non-interactive row is a false affordance (§4.5) */
.svc-cov__link::before {
  content: "";
  position: absolute;
  inset: -22px calc(var(--cell-bleed) * -1) -26px;
  z-index: -1;
  background: var(--navy-050);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}
.svc-cov__link::after {
  content: "";
  position: absolute;
  left: calc(var(--cell-bleed) * -1); top: -22px; bottom: -26px;
  z-index: -1;
  width: 3px;
  background: var(--gold);
  opacity: 0;
  transform: scaleY(.4); transform-origin: center;
  transition: opacity var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out);
}
.svc-cov__link:hover::before,
.svc-cov__link:focus-visible::before { opacity: 1; }
.svc-cov__link:hover::after,
.svc-cov__link:focus-visible::after { opacity: 1; transform: scaleY(1); }
.svc-cov__link:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; border-radius: var(--r-sm); }

/* the glyph is a meaningful graphic, so it takes --ink-3 (5.6:1) and never
   --navy-300 (3.04:1). It NEVER animates: it is a label. */
.svc-cov__glyph {
  grid-column: 1; grid-row: 1;
  display: block;
  width: 28px; height: 28px;
  color: var(--ink-3);
  transition: color var(--dur-2) var(--ease-out);
}
.svc-cov__glyph svg { width: 100%; height: 100%; display: block; }
.svc-cov__ix {
  grid-column: 2; grid-row: 1;
  align-self: center;
  font: 600 12px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: .08em;
  color: var(--ink-3);
}
.svc-cov__title {
  grid-column: 1 / -1;
  margin-top: var(--s-4);
  font: 600 20px/1.26 var(--font-display);
  letter-spacing: -.014em;
  color: var(--navy);
  overflow-wrap: anywhere;               /* the two 70-character outliers */
  text-wrap: balance;
}
.svc-cov__text { grid-column: 1 / -1; margin-top: 10px; }
.svc-cov__go {
  grid-column: 1 / -1;
  margin-top: var(--s-4);
  width: 16px; height: 16px;
  color: var(--ink-3);
  transition: color var(--dur-2) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.svc-cov__go svg { width: 16px; height: 16px; display: block; }
.svc-cov__link:hover  :is(.svc-cov__glyph, .svc-cov__ix, .svc-cov__go),
.svc-cov__link:focus-visible :is(.svc-cov__glyph, .svc-cov__ix, .svc-cov__go) { color: var(--navy); }
.svc-cov__link:hover .svc-cov__go,
.svc-cov__link:focus-visible .svc-cov__go { transform: translateX(4px); }
.svc-cov__link:hover .svc-cov__title,
.svc-cov__link:focus-visible .svc-cov__title {
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 5px;
  text-decoration-color: rgba(2, 45, 90, .35);
}

/* Void cells close the last row's rule and stop the dividers where they should.
   They are empty, aria-hidden, never in the accessibility tree, and hidden
   outright below 1024 where the grid reflows and they could land mid-list. A
   hole is NEVER filled with a photograph, a call to action, or an "and more"
   string (§4.4.3). */
.svc-cov__cell--void { display: none; }

@media (min-width: 768px) {
  .svc-cov__list { --cell-x: 20px; --cell-bleed: 20px; }
  .svc-cov__list[data-cols="2"],
  .svc-cov__list[data-cols="3"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .svc-cov__list[data-cols="2"] > .svc-cov__cell,
  .svc-cov__list[data-cols="3"] > .svc-cov__cell { border-left: 1px solid var(--line); }
  .svc-cov__list[data-cols="2"] > .svc-cov__cell:nth-child(2n + 1),
  .svc-cov__list[data-cols="3"] > .svc-cov__cell:nth-child(2n + 1) { border-left: 0; }
  .svc-cov__cell { padding-block: 24px 28px; }
  .svc-cov__cell--feature { grid-column: span 2; }
}
@media (min-width: 1024px) {
  .svc-cov__list { --cell-x: 24px; --cell-bleed: 24px; margin-top: var(--s-8); }
  .svc-cov__list[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* re-declared, because the rendered column count changed under it (§4.2.1) */
  .svc-cov__list[data-cols="3"] > .svc-cov__cell:nth-child(2n + 1) { border-left: 1px solid var(--line); }
  .svc-cov__list[data-cols="3"] > .svc-cov__cell:nth-child(3n + 1) { border-left: 0; }
  .svc-cov__cell { padding-block: 26px 30px; }
  .svc-cov__cell--void { display: grid; }
  .svc-cov__title { font-size: 21px; }
}
@media (min-width: 1180px) {
  .svc-cov__list { --cell-x: 28px; --cell-bleed: 28px; margin-top: var(--s-9); }
  .svc-cov__cell { padding-block: 28px 32px; }
  .svc-cov__title { font-size: 22px; }
}
@media (min-width: 1424px) {
  .svc-cov__list { --cell-x: 32px; --cell-bleed: 32px; }
  .svc-cov__cell { padding-block: 30px 34px; }
}

/* the feature cell is the same cell with more room, not a new component */
@media (min-width: 768px) {
  .svc-cov__cell--feature .svc-cov__glyph { width: 32px; height: 32px; }
  .svc-cov__cell--feature .svc-cov__title { font-size: 24px; }
  .svc-cov__cell--feature .svc-cov__text  { font: var(--t-body); max-width: 62ch; }
}

/* --- 4. how the work runs: DELETED (SPEC-SVC-R4 §4.5) -------------------
   .svc-proc, .svc-runs__* and .svc-run* are superseded by .s4-runs* /
   .s4-run*, which promote the CSS-drawn connector to a real inline SVG rail
   on a full-bleed tinted plane. service-next.php was their only consumer. */

/* --- 4b. the band plate: DELETED -------------------------------------
   .svc-plate was a full-bleed 220-300px photograph with a gold hairline,
   sitting BETWEEN the process band's intro and its steps. The client
   rejected it: it carried no copy, it cropped heads at the top of the
   frame, and it stretched a 1200px source across a 2000px viewport, so it
   read as a soft cinematic divider rather than as content.

   service-next.php was its only consumer, so the rules are removed rather
   than left on disk to be picked up again. The rule this build keeps:
   every photograph on an interior single is a composed element INSIDE a
   band, beside or under the copy that explains it. No edge-to-edge image
   rows. The `bleed` slot in inc/photos.php now has no caller on either
   single. (SPEC-SI-R3-AMEND §8 said not to touch this block; that ruling
   predates the client's verdict on the band it served.) */

/* the repeater bodies share one prose treatment across three objects: the
   surviving ledger (industry, about, thank-you, case study), the register grid
   and the stepped timeline. .svc-del__text stays because those four templates
   still render it (SPEC-SI-R3-AMEND §8.1). */
.svc-del__text, .svc-cov__text, .svc-run__text { font: var(--t-body-sm); max-width: 46ch; }
.svc-del__text { color: var(--ink-3); }
/* 8.9:1 on --paper and 8.2:1 on --navy-050 */
.svc-cov__text, .svc-run__text { color: var(--ink-2); }
.svc-run__text { max-width: none; }     /* the timeline track governs the measure */
@media (max-width: 767px) { .svc-run__text { max-width: 46ch; } }
:is(.svc-del__text, .svc-cov__text, .svc-run__text) :is(p, li) + :is(p, li) { margin-top: var(--s-2); }
:is(.svc-del__text, .svc-cov__text, .svc-run__text) :is(ul, ol) { padding-left: var(--s-5); list-style: disc outside; }

/* --- 5. platforms (SPEC-S1-R2 §8.1) -------------------------------------
   A rule-bounded strip on whatever ground it lands on, not a band, so it can
   never break the ground alternation on the ~18 services that publish
   svc_tools. Left-aligned, like everything else on the page. */
.svc-plat { padding-block: var(--s-10); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.svc-plat__grid { display: grid; gap: var(--s-6); }
.svc-plat__h {
  margin-top: var(--s-3);
  font: var(--t-d4);
  letter-spacing: -.012em;
  color: var(--navy);
}
@media (min-width: 1024px) {
  .svc-plat__grid { grid-template-columns: 240px minmax(0, 1fr); column-gap: var(--s-10); align-items: center; }
  .svc-plat__note { grid-column: 1 / -1; }
}
.svc-plat__set {
  display: flex; flex-wrap: wrap;
  align-items: center;
  gap: var(--s-5) var(--s-8);
  --rail-gap: 0px;                       /* .lg's padding-inline is driven by the
                                            rail variable, and the wrapped form
                                            has no rail: `gap` spaces it here. */
}
@media (min-width: 561px)  { .svc-plat__set { gap: var(--s-6) var(--s-9); } }
@media (min-width: 1024px) { .svc-plat__set { gap: var(--s-6) var(--s-9); } }

/* --- 5a. the marquee (TOOLS-01, re-homed TOOLS-04) ----------------------
   The rail itself is the shared .lgrail component and lives in next.css with
   its own timing, mask, pause and reduced-motion unwind. What stays here is
   only this band's placement of it: a flex/grid child holding a max-content
   track must be allowed to be narrower than that track, or it cannot clip and
   the row pushes the page sideways. That is the whole rule. */
.svc-plat__rail { min-width: 0; }

/* --- 5b. grouped form (TOOLS-01) ----------------------------------------
   Past eight tools a single row stops reading as a list and starts reading as
   a wall, so the band splits into the library's own groups. Grouped never
   animates: the headings are the structure, and sliding them defeats it. */
.svc-plat__groups { display: grid; gap: var(--s-6); }
@media (min-width: 768px) {
  .svc-plat__groups { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--s-9); }
}
.svc-plat__glabel {
  margin-bottom: var(--s-3);
  padding-bottom: var(--s-2);
  border-bottom: 1px solid var(--line);
  font: var(--t-cap);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
/* Not optional and not shrinkable: eight vendor logos under "Platforms we
   work in" is an implied-partnership claim, and this sentence is what makes
   it a factual statement about competence instead. */
.svc-plat__note {
  margin-top: var(--s-6);
  max-width: 68ch;
  font: var(--t-cap); font-size: 12px;
  color: var(--ink-3);
}

/* --- 6. FAQ (§5.6) ------------------------------------------------------ */
.svc-faq { padding-block: var(--section-y); background: var(--paper); }
/* .svc-faq--tight is DELETED. It fired when two adjacent bands shared a
   ground; under SPEC-S1-R2's and SPEC-I-R2's alternator that case cannot
   arise on either consumer, so the modifier had no reachable state left. */

.svc-faq__list { margin-top: var(--s-8); border-top: 1px solid var(--line); }
.svc-q { border-bottom: 1px solid var(--line); }
/* SPEC-C-R2 ruling 3: a question wraps to as many lines as it needs and the
   chevron stays on the FIRST line, so the row is aligned at the start, not
   the centre, and the chevron carries the half-leading of one 16/1.45 line
   (23.2 - 18 = 5.2, halved) as its own top offset. Nothing here clips or
   clamps: the remaining ellipsis on the service and industry FAQs is the
   52-character fa_svc_trim() cap in those two templates, not this rule. */
.svc-q__sum {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s-5);
  min-height: 64px; padding-block: var(--s-4);
  cursor: pointer; list-style: none;
  transition: color var(--dur-1) var(--ease-out);
}
.svc-q__sum::-webkit-details-marker { display: none; }
.svc-q__q { min-width: 0; font: 600 16px/1.45 var(--font-ui); color: var(--navy); overflow-wrap: anywhere; }
.svc-q__sum:hover .svc-q__q { color: var(--navy-500); }
.svc-q__chev {
  flex: 0 0 auto; width: 18px; height: 18px; margin-top: 3px; color: var(--navy-300);
  transition: transform var(--dur-2) var(--ease-io), color var(--dur-1) var(--ease-out);
}
.svc-q[open] .svc-q__chev { transform: rotate(180deg); color: var(--navy); }
.svc-q__sum:hover .svc-q__chev { color: var(--navy); }
.svc-q__sum:focus-visible { outline: 2px solid var(--navy); outline-offset: 3px; border-radius: var(--r-xs); }

.svc-q__a { padding-bottom: var(--s-6); }
.svc-q__a p { font: var(--t-body); color: var(--ink-3); max-width: 62ch; }
.svc-q__a p + p { margin-top: var(--s-4); }

/* Progressive enhancement only. `interpolate-size: allow-keywords` on :root
   is refused (§16 item 12): a document-wide change to how `auto` resolves,
   for one band's disclosure animation. Where ::details-content does not
   exist the disclosure snaps, which is what <details> has always done. */
@supports selector(::details-content) {
  .svc-q::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size var(--dur-3) var(--ease-io), content-visibility var(--dur-3) allow-discrete;
  }
  .svc-q[open]::details-content { block-size: auto; }
}

/* --- 6b. the round-two FAQ band (SPEC-S1-R2 §8.2) ------------------------
   Everything above stays as SPEC-S1 wrote it, because both consumers render
   .svc-faq / .svc-faq__list / .svc-q* verbatim. SPEC-I-R2 §A10 gave the
   industry FAQ this same geometry rather than a second copy of it, so the
   round-two rules are scoped to the two page classes and nothing else on the
   site moves.

   container--narrow is gone here: it broke the page's left spine for exactly
   one band. This band takes band 3's two-column geometry, so the two ruled
   bands register to one another. */
:is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-faq--paper { background: var(--paper); }
:is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-faq--wash  { background: var(--wash); }
:is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-faq__grid { display: grid; gap: var(--s-9); }
:is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-faq__aside { max-width: none; }
:is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-faq__list { margin-top: 0; }
@media (min-width: 1024px) {
  :is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-faq__grid { grid-template-columns: 320px minmax(0, 1fr); column-gap: var(--s-10); }
  :is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-faq__aside .sec__intro { max-width: 34ch; }

  /* STICKY SHORT COLUMN. Five open-and-shut accordions are much taller than
     the aside, and taller again once a reader opens one, so this is the band
     where the intro would otherwise be off-screen for the whole read. The
     grid has no align-items, hence align-self here as well as position. */
  :is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-faq__aside {
    position: sticky;
    top: calc(var(--header-h, 80px) + 24px);
    align-self: start;
  }
}
@media (min-width: 1180px) {
  :is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-faq__grid { grid-template-columns: 380px minmax(0, 1fr); column-gap: var(--s-11); }
}
:is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-q__sum {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 20px;
  align-items: start;
  gap: var(--s-5);
  min-height: 68px;
  padding-block: var(--s-5);
}
:is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-q__q { font: 600 18px/1.4 var(--font-display); letter-spacing: -.010em; }
/* 18/1.4 is a 25.2px line; (25.2 - 18) / 2 puts the chevron on its centre */
:is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-q__chev { margin-top: 4px; }
/* the open row: a --paper fill bleeding 20px past the text edge and a 3px
   gold left edge on the same pseudo-element. This is SPEC-04-R2's active-row
   treatment, which is why the band ground alternates: the fill needs
   something to be lighter than. Gold is an edge, never a fill, never behind
   text, and it appears exactly once in the band. */
:is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-q { position: relative; isolation: isolate; }
:is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-q[open]::before {
  content: "";
  position: absolute; inset: 0 -20px;
  z-index: -1;
  background: var(--paper);
  border-radius: var(--r-sm);
  border-left: 3px solid var(--gold);
}
:is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-faq--paper .svc-q[open]::before { background: var(--navy-050); }

/* --- 7. related: DELETED (SPEC-SVC-R4 §4.11) ----------------------------
   The .svc-rel__* ledger rows were already superseded by the .rel-card grid
   in round three and both are superseded now by .s4-rel__*, which adds the
   per-card mark. service-next.php was the only consumer of either. */

/* --- 8. THE CLOSING BAND: MOVED to assets/next.css ---------------------
   The band is site-wide and templates/home-next.php loads next.css ONLY, so a
   component living here reached thirteen templates and not the fourteenth:
   the homepage rendered the closer's markup with no rules at all. The whole
   .svc-cta block, its focus ring, its reduced-motion and prefers-contrast
   entries and its .r--plate will-change now live in next.css, which every
   template on the site loads. Nothing about the band is styled from this file
   any more; do not add a .svc-cta rule here.

   The 1120px seam arithmetic that used to compute a --seam is deleted with the
   full-bleed plane it positioned. */

/* --- tier 3 (§11.1) -----------------------------------------------------
   A service with no structured fields at all renders one prose band in
   .legal__body, the site's one long-form treatment, rather than a new one.
   This is the one band that keeps the narrow measure, because it is the one
   band that is long-form prose. */
/* .svc-raw is DELETED with the tier-3 branch that emitted it. That branch fired
   on zero of 100 posts and its job - render post_content when there is nothing
   else - is now the Overview band's default (SPEC-SI-R3 §4.2.3). */

/* --- in-page anchor targets: MOVED (SPEC-SVC-R4 §12.2 item 12) ----------
   The five ids are #s4-fit, #s4-cov, #s4-runs, #s4-why, #s4-proof, #s4-price
   and #s4-faq now, and their scroll-margin rule travels with them into the
   R4 block at the foot of this file, in the same commit as the renames. */

/* =========================================================================
   SERVICES HUB  (SPEC-S2)
   A typographic directory. No cards around services, no per-service imagery,
   no pattern in a link band: a directory fills its measure edge to edge, so
   SPEC-00 §3.3 rule 5 leaves no ground to paint. The 2px rule under each <h3>
   is the only structure and it is doing all the grouping work.
   ========================================================================= */
.svch {
  position: relative;
  isolation: isolate;
  padding-block: var(--section-y);
  background: var(--paper);
}
.svch--wash  { background: var(--wash); }
/* only when the §8.5d `More services` band renders and puts two --paper
   sections back to back */
.svch--tight { padding-top: 0; }
.svch--tight .svch__head { padding-top: var(--s-10); border-top: 1px solid var(--line-2); }

/* <=1023 the head is SPEC-00 §10's .sec__head, stacked, unchanged. Splitting
   at 768 would set "The books, the payroll, and the close." to four lines in
   a 340px half. Worth 112px per band, five bands, 560px on the page. */
@media (min-width: 1024px) {
  .svch__head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--s-9);
    max-width: none;
  }
  .svch__head > div         { flex: 1 1 auto; }
  .svch__head > .sec__intro { flex: 0 1 46ch; margin-top: 0; max-width: 46ch; }
}

.svch__empty {
  margin-top: var(--s-7);
  font: var(--t-body); color: var(--ink-3); max-width: 62ch;
}

/* the band index, inline before the eyebrow's gold tick. It ties the head to
   the plate's 72px numeral without putting a second large numeral in the text
   grid. aria-hidden, decorative, never announced. */
.hubband__i {
  font-variant-numeric: tabular-nums;
  color: var(--navy-300);
  margin-right: var(--s-2);
}

/* =========================================================================
   SERVICES HUB, ROUND TWO  (SPEC-S2-R2)  |  templates/services-hub.php
   One ruled sheet with a navy masthead. Nothing is hidden, nothing is
   filtered, nothing moves on its own: every one of the hundred titles is a
   complete, untruncated, unclamped text node at every breakpoint, so the
   browser's own find covers the whole document.

   .sm__h, .sm__list, .e4__link, .ph-set, .phf, .ph-band and .ph-pair are all
   left alone above; they have other consumers.
   ========================================================================= */

/* --- 1. the hero's contents index (SPEC-IH-R3 §3a, §12.2) ----------------
   .hubhero is DELETED; the hub hero is .ihero .ihero--hub. The contents
   component survives the migration intact and is recoloured for paper: it is
   the one place on an interior page where the homepage rail's numbered-ledger
   idea is carried over on purpose (§8 item 15). It now serves three hubs -
   services, industries and case studies. */
.hubtoc__label {
  font: var(--t-eyebrow);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-3);
  opacity: 0;
  animation: fade 360ms var(--ease-out) 320ms both;
}
.hubtoc__list { margin-top: var(--s-4); border-top: 1px solid var(--navy-100); list-style: none; padding: 0; }
.hubtoc__item { border-bottom: 1px solid var(--navy-100); }
.js .hubtoc__item { opacity: 0; animation: hubtoc-in 400ms var(--ease-out) calc(460ms + var(--i, 1) * 70ms) both; }
@keyframes hubtoc-in { from { opacity: 0; transform: translate3d(0, 12px, 0); } to { opacity: 1; transform: none; } }

.hubtoc__link {
  position: relative;
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr) 16px;
  align-items: center;
  column-gap: var(--s-1); row-gap: 6px;
  min-height: 72px;
  padding: 16px var(--gutter);
  margin-inline: calc(var(--gutter) * -1);
  text-decoration: none;
}
/* fill and gold edge, both gated by OPACITY so reduced motion cannot reveal
   every edge on the page at once */
.hubtoc__link::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -1;
  background: rgba(2, 45, 90, .04);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}
.hubtoc__link::after {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  z-index: -1;
  width: 3px;
  background: var(--gold);
  opacity: 0;
  transform: scaleY(.4);
  transition: opacity var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out);
}
.hubtoc__link:hover::before, .hubtoc__link:focus-visible::before { opacity: 1; }
.hubtoc__link:hover::after,  .hubtoc__link:focus-visible::after  { opacity: 1; transform: scaleY(1); }
.hubtoc__num {
  grid-column: 1; grid-row: 1;
  font: 600 22px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
  /* --navy-300 on paper is UI-only and permitted at this size (§10.6) */
  color: var(--navy-300);
  transition: color var(--dur-2) var(--ease-out);
}
.hubtoc__name {
  grid-column: 2; grid-row: 1;
  font: 600 20px/1.24 var(--font-display);
  letter-spacing: -.016em;
  color: var(--navy);
}
.hubtoc__desc {
  grid-column: 1 / -1; grid-row: 2;
  font: var(--t-cap);
  color: var(--ink-3);
}
.hubtoc__go {
  grid-column: 3; grid-row: 1;
  display: block;
  color: var(--navy-300);
  transition: color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.hubtoc__go svg { width: 16px; height: 16px; display: block; }
.hubtoc__link:hover .hubtoc__num, .hubtoc__link:focus-visible .hubtoc__num { color: var(--navy); }
.hubtoc__link:hover .hubtoc__go,  .hubtoc__link:focus-visible .hubtoc__go  { color: var(--navy-500); transform: translateX(4px); }

@media (min-width: 561px) {
  .hubtoc__desc { grid-column: 2 / 3; }
}
@media (min-width: 1024px) {
  .hubtoc__link { grid-template-columns: 52px minmax(0, 1fr) 16px; min-height: 80px; padding: 16px; margin-inline: -16px; }
  .hubtoc__num { font-size: 26px; }
}
@media (min-width: 1180px) {
  .hubtoc__name { font-size: 22px; }
}

/* --- 2. the band plate (§4) ---------------------------------------------
   A full-bleed row split by the gold seam: a navy panel carrying the band's
   index numeral and name, and the photograph filling the rest and bleeding
   off the viewport edge. It is a direct child of the <section>, so there is no
   `margin-inline: calc(50% - 50vw)` and no scrollbar rounding to overflow the
   document at 375.

   The panel's padding puts its text on the container's own left text edge, so
   `01` sits in the same vertical as the h2 above it and the directory below
   it. That alignment is the whole reason the plate does not read as a banner.

   ≤1023 the panel becomes an absolutely positioned overlay on the photograph:
   a 280px navy track over a 335px measure would leave the photograph a
   letterbox. */
.hubplate {
  position: relative;
  margin-top: var(--s-7);
  height: 180px;
  overflow: clip;
  isolation: isolate;
}
.hubplate__seam {
  position: absolute; left: 0; right: 0; top: 0;
  z-index: var(--z-glass);
  height: 3px;
  background: linear-gradient(90deg, var(--gold) 0 64%, var(--gold-600) 100%);
}
.hubplate__media { position: relative; height: 100%; }
.hubplate__media picture { position: absolute; inset: 0; display: block; }
.hubplate__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: var(--po, 50% 44%);
}
.hubplate__media::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(1, 30, 61, .28) 0, transparent 40%,
                                      transparent 58%, rgba(1, 30, 61, .52) 100%);
}
.hubplate__panel {
  position: absolute; left: 0; bottom: 0;
  z-index: var(--z-content);
  display: flex; align-items: baseline; gap: 14px;
  padding: 12px var(--s-5) 12px var(--gutter);
  background: rgba(1, 30, 61, .90);
}
.hubplate__num {
  font: 600 32px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.04em;
  color: rgba(255, 255, 255, .40);
}
.hubplate__label {
  font: var(--t-eyebrow);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--gold);                    /* >= 8.6:1 on the .90 scrim */
}
@media (min-width: 561px)  { .hubplate { height: 200px; margin-top: var(--s-7); } }
@media (min-width: 1024px) { .hubplate { height: 240px; margin-top: var(--s-8); } }
@media (min-width: 1024px) {
  .hubplate {
    display: grid;
    grid-template-columns: 280px 4px minmax(0, 1fr);
  }
  .hubplate__panel {
    position: static;
    display: flex; flex-direction: column; justify-content: flex-end;
    align-items: flex-start;
    gap: 10px;
    padding: 0 var(--s-8) var(--s-7) max(var(--gutter), calc(50% - (var(--container) / 2) + var(--gutter)));
    background: radial-gradient(140% 120% at 12% 0%, #063A70 0%, #022D5A 52%, #011E3D 100%);
  }
  .hubplate__seam {
    position: static;
    height: auto;
    background: linear-gradient(180deg, var(--gold) 0 64%, var(--gold-600) 100%);
  }
  .hubplate__num { font-size: 56px; color: rgba(255, 255, 255, .22); }
  .hubplate__media::after {
    background: linear-gradient(90deg, rgba(2, 45, 90, .88) 0, rgba(2, 45, 90, .36) 120px,
                                       rgba(2, 45, 90, .10) 260px, rgba(2, 45, 90, .06) 100%);
  }
  /* the flip is structural, not decorative: the panel and the photograph
     change sides and the dissolve mirrors with them */
  .hubplate--flip { grid-template-columns: minmax(0, 1fr) 4px 280px; }
  .hubplate--flip .hubplate__panel {
    order: 3;
    align-items: flex-end;
    text-align: right;
    padding: 0 max(var(--gutter), calc(50% - (var(--container) / 2) + var(--gutter))) var(--s-7) var(--s-8);
  }
  .hubplate--flip .hubplate__media { order: 1; }
  .hubplate--flip .hubplate__seam  { order: 2; }
  .hubplate--flip .hubplate__media::after {
    background: linear-gradient(270deg, rgba(2, 45, 90, .88) 0, rgba(2, 45, 90, .36) 120px,
                                        rgba(2, 45, 90, .10) 260px, rgba(2, 45, 90, .06) 100%);
  }
}
@media (min-width: 1180px) {
  .hubplate { height: 260px; grid-template-columns: clamp(280px, 26vw, 420px) 4px minmax(0, 1fr); }
  .hubplate--flip { grid-template-columns: minmax(0, 1fr) 4px clamp(280px, 26vw, 420px); }
  .hubplate__num { font-size: 72px; }
}

/* the plate reveal, the one new keyframe on the page: the seam grows down,
   then the photograph is uncovered outward from it */
@keyframes plate-wipe  { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0); } }
@keyframes plate-wipeb { from { clip-path: inset(0 0 0 100%); } to { clip-path: inset(0); } }
@keyframes plate-scale { from { transform: scale(1.04); } to { transform: none; } }
.js .hubplate__media { clip-path: inset(0 100% 0 0); }
.js .hubplate--flip .hubplate__media { clip-path: inset(0 0 0 100%); }
.js .hubplate.is-in .hubplate__media { animation: plate-wipe 720ms var(--ease-soft) 120ms both; }
.js .hubplate--flip.is-in .hubplate__media { animation: plate-wipeb 720ms var(--ease-soft) 120ms both; }
.js .hubplate.is-in .hubplate__media img { animation: plate-scale 900ms var(--ease-soft) 120ms both; }

/* --- 3. the directory (§5) ----------------------------------------------
   `columns` stays: grid was measured at 1,056px against columns' 880px for
   Accounting, because each grid row is as tall as its tallest block. The
   break-inside guard, the 44/63 row pitch and the DOM order all stay, so every
   number in SPEC-S2 §5.4 remains valid.

   What is new is four levels of hierarchy where there was one: a hanging index
   on every group, a hairline under every row, a column-rule drawing the sheet
   through the whole band, and a lead row in the display face. */
.hubdir {
  margin-top: var(--s-8);
  column-gap: var(--s-9);
  column-fill: balance;
  column-rule: 1px solid var(--line-2);
  overflow-wrap: break-word;             /* 375: a long title wraps, never overflows */
}
@media (min-width: 768px)  { .hubdir { columns: 2; } }
@media (min-width: 1024px) { .hubdir { columns: 3; } }

.hubdir__grp {
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  margin-bottom: var(--s-11);            /* 80: at 40 the group gap reads as one more row rule */
}
.hubdir__grp:last-child { margin-bottom: 0; }

.hubdir__h {
  display: flex; align-items: baseline; gap: var(--s-3);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--navy-100);
  font: var(--t-d4);
  color: var(--navy);
}
.hubdir__i {                             /* the hanging index, aria-hidden */
  flex: 0 0 auto;
  font: 600 12px/1 var(--font-ui);
  letter-spacing: .16em;
  font-variant-numeric: tabular-nums;
  color: var(--navy-300);
}
.hubdir__list { margin-top: var(--s-2); }
/* No padding-left transition on hover: that reflows a row inside a `columns`
   container and at a hundred rows it is a genuine jank source. The fill and
   the gold edge carry the state instead, and neither reflows. */
.hubdir__list a {
  position: relative;
  display: flex; align-items: center;
  min-height: 44px;
  padding: 10px 12px;
  margin-inline: -12px;
  border-bottom: 1px solid var(--line-2);
  font: var(--t-body-sm);
  color: var(--ink-2);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease-out), background-color var(--dur-2) var(--ease-out);
}
.hubdir__list li:last-child a { border-bottom: 0; }
.hubdir__list a::before {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 2px;
  background: var(--gold);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}
.hubdir__list a:hover, .hubdir__list a:focus-visible { color: var(--navy); background: var(--navy-050); }
.hubdir__list a:hover::before, .hubdir__list a:focus-visible::before { opacity: 1; }
.hubdir__list a:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; border-radius: 4px; }
/* the group's own hub service, in the display face. It publishes the editorial
   order inc/services-map.php already asserts and claims nothing. */
.hubdir__list a.is-lead {
  min-height: 46px;
  font: 600 17px/1.36 var(--font-display);
  color: var(--navy);
}

/* the bypass, and the return link */
.skip-inline { z-index: var(--z-content); }
.skip-inline:focus { position: static; display: inline-flex; align-items: center; min-height: 44px; margin-top: var(--s-6); }
.hubband__back {
  margin-top: var(--s-9);
  padding-top: var(--s-6);
  border-top: 1px solid var(--line);
  font: var(--t-label);
}
.hubband__back a {
  display: inline-flex; align-items: center; gap: var(--s-2);
  min-height: 44px;
  color: var(--navy);
  text-decoration: none;
}
.hubband__back a::before { content: "\2191"; transition: transform var(--dur-1) var(--ease-out); }
.hubband__back a:hover { text-decoration: underline; text-underline-offset: 4px; }
.hubband__back a:hover::before { transform: translateY(-2px); }

/* --- 4. Cleveland: rows, not cards (§6.5) -------------------------------- */
.hubloc {
  display: grid;
  gap: 0 var(--s-9);
  margin-top: var(--s-8);
  border-top: 1px solid var(--line);
}
@media (min-width: 768px) { .hubloc { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.hubloc__link {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 16px;
  align-items: center;
  column-gap: var(--s-4); row-gap: 6px;
  min-height: 96px;
  padding: 22px 16px;
  margin-inline: -16px;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
}
.hubloc__link::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -1;
  background: var(--navy-050);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}
.hubloc__link::after {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  z-index: -1;
  width: 3px;
  background: var(--gold);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}
.hubloc__link:hover::before, .hubloc__link:focus-visible::before,
.hubloc__link:hover::after,  .hubloc__link:focus-visible::after { opacity: 1; }
.hubloc__link:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; border-radius: 4px; }
.hubloc__name { grid-column: 1; grid-row: 1; font: var(--t-d4); color: var(--navy); }
/* The clamp is gone at >=561: this page's contract is that nothing is hidden,
   and at 46ch the 80-character teaser sets to two lines naturally. It is kept
   only at <=560, where it clips a teaser and never a link label. */
.hubloc__desc { grid-column: 1 / 2; grid-row: 2; font: var(--t-cap); color: var(--ink-3); max-width: 46ch; }
@media (max-width: 560px) {
  .hubloc__desc {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
    overflow: hidden;
  }
}
.hubloc__go {
  grid-column: 2; grid-row: 1 / span 2;
  align-self: center;
  display: block;
  color: var(--ink-3);
  transition: color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.hubloc__go svg { width: 16px; height: 16px; display: block; }
.hubloc__link:hover .hubloc__go, .hubloc__link:focus-visible .hubloc__go { color: var(--navy); transform: translateX(4px); }

/* --- 5. the cycles ledger, flat (§6.6) ----------------------------------
   next.css's .cyc verbatim, with the panel taken off: the Run band is already
   --paper, so a --paper panel inside it would be invisible, and the aside, the
   figure and the slides are all absent, which is what makes this cost zero new
   JavaScript. next.js module 8 guards on .cyc and its cSlides loop tolerates
   an empty NodeList. */
.cyc--flat {
  --cyc-pad: 0px;
  margin-top: var(--s-8);
  background: none;
  border-top: 0;
  border-bottom: 0;
}
@media (min-width: 1024px) { .cyc--flat { margin-top: var(--s-9); } }
/* the 1024-1179 compact row at every width >= 768, so a hub row is 104px and
   the four rows plus five rules are 421 */
@media (min-width: 768px) {
  .cyc--flat .cyc__link {
    grid-template-columns: 64px 40px minmax(0, 1fr) 18px;
    column-gap: var(--s-5);
    padding-block: 26px;
  }
  .cyc--flat .cyc__num   { font-size: 44px; }
  .cyc--flat .cyc__glyph svg { width: 40px; height: 40px; }
  .cyc--flat .cyc__name  { font-size: 22px; }
  .cyc--flat .cyc__desc  { grid-column: 3; grid-row: 2; margin-top: var(--s-2); align-self: auto; max-width: 40ch; }
  .cyc--flat .cyc__arw   { grid-column: 4; grid-row: 1 / span 2; }
  .cyc--flat .cyc__arw svg { width: 18px; height: 18px; }
}
/* module 8 leaves is-on on the last row a pointer entered. On the homepage
   that drives a photograph; here there is none, so a row must not stay lit. */
.cyc--flat .cyc__link.is-on:not(:hover):not(:focus-visible)::before,
.cyc--flat .cyc__link.is-on:not(:hover):not(:focus-visible)::after { opacity: 0; }
.cyc--flat .cyc__link.is-on:not(:hover):not(:focus-visible) .cyc__num { color: rgba(2, 45, 90, .16); }
.cyc--flat .cyc__link.is-on:not(:hover):not(:focus-visible) .cyc__glyph,
.cyc--flat .cyc__link.is-on:not(:hover):not(:focus-visible) .cyc__arw { color: var(--ink-3); }

/* --- 6. the platform sheet (§6.6) ---------------------------------------
   Fourteen logos into seven columns is exactly two full rows with no orphan,
   which is why seven and not six or eight. At 1023 it is 4 x 4 minus two, and
   at 560 3 x 5 minus one: visibly deliberate on a ruled grid. --rail-gap is
   set here because .lg's padding-inline is driven by it and there is no rail
   on this page. */
.hubplat {
  margin-top: var(--s-11);
  padding-top: var(--s-10);
  border-top: 1px solid var(--line);
}
/* .eyebrow supplies the flex box and the gold tick; the case and tracking are
   reset because this is a sentence with a full stop and not a label, and it
   ships verbatim (SPEC-S2-R2 §6.6). */
.hubplat__label {
  font: var(--t-lead);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-2);
  max-width: 46ch;
}
.hubplat__grid {
  margin-top: var(--s-7);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--line-2);
  border-left: 1px solid var(--line-2);
  --rail-gap: 0px;
  --lg-scale: .84;
}
@media (min-width: 561px)  { .hubplat__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .hubplat__grid { grid-template-columns: repeat(7, minmax(0, 1fr)); --lg-scale: .92; } }
.hubplat__cell {
  display: grid; place-items: center;
  min-height: 72px;
  padding: var(--s-4);
  border-right: 1px solid var(--line-2);
  border-bottom: 1px solid var(--line-2);
}
@media (min-width: 1024px) { .hubplat__cell { min-height: 84px; } }
.hubplat__note {
  margin-top: var(--s-6);
  max-width: 62ch;
  font: var(--t-cap);
  color: var(--ink-3);
}

/* --- no-JS (§8.5) -------------------------------------------------------
   .is-in never lands without the observer, so anything that RESTS at a
   non-final value has to be forced. Nothing on this page is only reachable
   through a script. */
html:not(.js) .hubplate__media { clip-path: none; }
html:not(.js) .hubtoc__item { opacity: 1; }


/* =========================================================================
   INDUSTRIES, ROUND TWO  (SPEC-I-R2)
   templates/industry-next.php  |  templates/industries-hub.php

   This block replaces the SPEC-I1 and SPEC-I2 blocks entirely. What went with
   them: the old rounded hero frame carrying the page's only --sh-4,
   the .ind-hero__cue, both container--narrow spine breaks, the .svc-del dot
   field, the .ind-std bordered pill (the site's only pill), the .ind-rel card
   grid with its lift and shadow, the closing .ph-sect photograph, the
   .svc-faq--tight modifier and the hub's twelve-tile grid with its glass
   twelfth cell.

   The hero and the closing CTA are NOT here. Both templates render .ihero
   from SPEC-IH-R3 and .svc-cta from SPEC-S1-R2 by reuse; a parallel .indhero
   / .indcta is SPEC-00 §10's forbidden duplicate.

   Band 3 and band 7 are likewise not here: the pains band and the FAQ render
   SPEC-S1-R2's .svc-del* / .svc-faq* / .svc-q* geometry, whose round-two rules
   are scoped `:is(.page-service, .page-industry, .page-about, .page-thank-you)` above rather than copied.
   ========================================================================= */

/* --- the hero deltas: DELETED (SPEC-IH-R3 §12.1) ------------------------
   The industry single is .ihero .ihero--single .ihero--ind and the hub is
   .ihero .ihero--hub. The plane's 900px upscale guard goes with the plane:
   the plate is 340 to 496 CSS px at every width, so a 900px master is never
   upscaled and no cap is needed. The hub's 104px stat rail goes with the
   rail. */

/* --- single, band 2: SUPERSEDED BY SPEC-IO-R3 --------------------------
   The .indopen* block that stood here is deleted in full. Both of its shapes
   failed for the same reason: the largest type on the band was a paragraph, so
   the band had no headline and nothing below it could step down. Its
   replacement is the .io- block at the end of this file (the Opening Record).
   Only the ground ladder survives, because bands 4 to 8 share it. */
/* Every band on this template takes --section-y and one of two grounds, so the
   ladder is declared once rather than five times. */
.i4-work, .i4-std, .indrel { padding-block: var(--band-y, var(--section-y)); }
.i4-work--paper, .indsvc--paper, .i4-std--paper, .indrel--paper { background: var(--paper); }
.i4-work--wash,  .indsvc--wash,  .i4-std--wash,  .indrel--wash  { background: var(--wash); }

/* --- single, band 3: the pains list's gold mark (§A6 delta 1) -----------
   The service page spends its one device on a full-bleed --paper panel and
   hangs the gold mark on the panel's top rule. Here the band sits on --wash
   between two --paper bands, so the ground change is already the register
   change and there is no panel: the 72x3 mark moves onto the list's own top
   rule, aligned to the container's left text edge. One device per band. */
.page-industry .svc-cov__list { position: relative; }
.page-industry .svc-cov__list::before {
  content: "";
  position: absolute; left: var(--cell-x); top: -1px;
  z-index: var(--z-content);
  width: 56px; height: 3px;
  background: var(--gold);
}
@media (min-width: 1024px) { .page-industry .svc-cov__list::before { width: 72px; } }
/* the list's own top rule, which on the service page is the panel's edge */
.page-industry .svc-cov__list { border-top: 1px solid var(--line); }
.page-industry .svc-cov__list > .svc-cov__cell:first-child { border-top: 0; }
@media (min-width: 768px) {
  .page-industry .svc-cov__list[data-cols="2"] > .svc-cov__cell:nth-child(-n + 2),
  .page-industry .svc-cov__list[data-cols="3"] > .svc-cov__cell:nth-child(-n + 2) { border-top: 0; }
}
@media (min-width: 1024px) {
  .page-industry .svc-cov__list[data-cols="3"] > .svc-cov__cell:nth-child(-n + 2) { border-top: 1px solid var(--line); }
  .page-industry .svc-cov__list[data-cols="3"] > .svc-cov__cell:nth-child(-n + 3) { border-top: 0; }
}

/* --- THE DUO (SPEC-SI-R3 §3, object 3) ---------------------------------
   These were .indwork__list / __row / __term / __desc and every value below is
   the shipped rule under a new name. The rename is the whole change, and it is
   the point: service-next.php's reinstated Approach band renders on THIS
   component rather than on a fourth copy of it, so the two single templates
   draw from one set of five row objects and a reader moving between them meets
   one system. .indwork survives as the band wrapper and .indwork__case as the
   client note.

   Two columns of ruled cells, row-major. Reading order is DOM order and DOM
   order is row-major, so the sequence is unambiguous without decoding. This is
   also what keeps the band distinct from the register grid above it: no
   vertical dividers, no glyph, no index numeral, no link affordance, and a
   photograph in the aside that the register grid never has.

   An ODD item count leaves the last cell alone in the left column with the
   right column empty. That is correct for a ruled grid and nothing is put in
   the hole. */
.i4-work__list { margin-top: var(--s-8); border-top: 1px solid var(--line); }
/* 22 at 375 to 28 at 1440, and the column gap 40 to 80, as clamps rather than
   four breakpoints: the pitch is a continuous quantity and stepping it three
   times buys nothing a reader can see. */
.i4-work__row { padding-block: clamp(22px, 2vw, 28px); border-bottom: 1px solid var(--line); }
@media (min-width: 768px) {
  .i4-work__list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(var(--s-8), 5.9vw, var(--s-11));
  }
}

.i4-work__term {
  font: 600 21px/1.26 var(--font-display);
  letter-spacing: -.014em;
  color: var(--navy);
  max-width: 24ch;
  overflow-wrap: anywhere;              /* the CMS strings run to 34 chars */
}
@media (min-width: 1180px) { .i4-work__term { font-size: 24px; } }
/* The <dd> is a two-track grid: the 40px user-agent indent that used to sit
   empty beside every paragraph becomes a 24px mark plus a 12px gutter, which
   is 36px, so the paragraph itself barely moves and the gutter stops being a
   hole. margin-inline-start is zeroed explicitly because that indent is the
   thing being replaced. */
.i4-work__desc {
  margin: 10px 0 0;
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  column-gap: 12px;
  align-items: start;
  font: var(--t-body-sm);
  color: var(--ink-2);
}
/* --navy-300 is 3.4:1, the non-text UI floor, and the mark is never the only
   signal: the term above it carries the meaning. It does not animate. */
.i4-work__ico     { display: block; width: 24px; height: 24px; color: var(--navy-300); }
.i4-work__ico svg { display: block; width: 100%; height: 100%; }
.i4-work__text    { min-width: 0; max-width: 42ch; }
.i4-work__text :is(p, li) + :is(p, li) { margin-top: var(--s-2); }
.i4-work__text :is(ul, ol) { margin-top: var(--s-2); padding-left: var(--s-5); list-style: disc outside; }

/* --- THE APPROACH BAND and THE CWS NOTE: BOTH DELETED -------------------
   .svc-why* is superseded by .s4-why* (SPEC-SVC-R4 §4.6) and service-next.php
   was its only consumer. .indwork__case is superseded by the case TILE on the
   navy proof band (SPEC-IND-R4 §4.8.2): the one piece of named client proof in
   the industries tree stops being a note at the foot of a duo on one of eleven
   pages and becomes a tile on all eleven. industry-next.php was its only
   consumer. */

/* --- single, band 5: the services strip (§A8) ---------------------------
   Rule-bounded, on whatever ground band 4 took, so it can never break the
   alternation: a strip is not a band flip. The fill and the gold edge are both
   gated by OPACITY, never by a padding-left transition, which reflows. */
.indsvc { padding-block: var(--s-10); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.indsvc__grid { display: grid; gap: var(--s-6); }
@media (min-width: 1024px) {
  .indsvc__grid { grid-template-columns: 240px minmax(0, 1fr); column-gap: var(--s-10); align-items: start; }
}
/* .eyebrow supplies the flex box and the gold tick; the case and tracking are
   reset because this is a sentence and not a label, the same treatment
   .hubplat__label ships. */
.indsvc__label {
  font: var(--t-d4);
  letter-spacing: -.012em;
  text-transform: none;
  color: var(--navy);
  align-items: baseline;
}
.indsvc__list { display: grid; border-top: 1px solid var(--line-2); }
@media (min-width: 768px) { .indsvc__list { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--s-9); } }
.indsvc__list a {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 16px;
  align-items: center;
  column-gap: var(--s-4);
  min-height: 56px;
  padding: 14px 16px;
  margin-inline: -16px;
  border-bottom: 1px solid var(--line-2);
  font: 600 16px/1.4 var(--font-ui);
  color: var(--navy);
  text-decoration: none;
}
.indsvc__list a svg {
  width: 16px; height: 16px; display: block;
  color: var(--ink-3);
  transition: color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
/* The row's own margin-inline already bleeds its box 16px past the text edge,
   so the fill is `inset: 0` and not the spec's `inset: 0 -16px`, which would
   bleed a second time and put the fill 32px out. This is .hubloc__link's
   construction, which is the one already shipping. */
.indsvc__list a::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -1;
  background: var(--navy-050);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}
.indsvc__list a::after {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  z-index: -1;
  width: 3px;
  background: var(--gold);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}
.indsvc__list a:hover::before, .indsvc__list a:focus-visible::before,
.indsvc__list a:hover::after,  .indsvc__list a:focus-visible::after { opacity: 1; }
.indsvc__list a:hover svg, .indsvc__list a:focus-visible svg { color: var(--navy); transform: translateX(4px); }
.indsvc__list a:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; border-radius: var(--r-sm); }
.indsvc__all { margin-top: var(--s-6); }

/* --- single, band 6: the reference docket (§A9) -------------------------
   auto-fit with a 220px floor is what makes the band self-adjust: five items
   give five tracks, four give four, three give three. There is never an orphan
   cell and never a void, at any count from one to five, without a single
   count-dependent selector. */
.i4-std__set {
  margin-top: var(--s-8);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line-2);
}
@media (max-width: 1023px) { .i4-std__set { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); } }
@media (max-width: 560px)  { .i4-std__set { grid-template-columns: minmax(0, 1fr); } }
.i4-std__item {
  padding: var(--s-5) var(--s-6) var(--s-6);
  border-right: 1px solid var(--line-2);
  border-bottom: 1px solid var(--line);
}
/* nowrap is load-bearing for the same reason the deleted pill needed it:
   `Uniform Guidance (2 CFR 200)` is 28 characters and must not break. At 13px
   Mulish 700 with .06em that is roughly 232px, so the term is allowed to
   overflow its 220px track into the cell's 24px right padding. */
.i4-std__term {
  font: 600 13px/1.3 var(--font-ui);
  font-variant-numeric: tabular-nums;
  letter-spacing: .06em;
  color: var(--navy);
}
/* SPEC-SI-R3 §5.1 item 6: nowrap from 561 up, where the cell is at least 220px
   wide and `Uniform Guidance (2 CFR 200)` fits into its 24px right padding.
   Below that the docket is one full-measure cell per row and a 40-character
   term at 13px Mulish 700 with .06em runs past its own padding, so it wraps. */
@media (min-width: 561px) { .i4-std__term { white-space: nowrap; } }
/* The gloss is a two-track grid now: the <dd>'s inherited indent is replaced
   by an explicit 22px icon column plus a 12px gutter, so the space that used
   to sit empty beside the paragraph holds a drawn mark instead. margin-inline
   is zeroed explicitly because the UA indent is what we are replacing. */
.i4-std__gloss {
  margin: var(--s-3) 0 0;
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  column-gap: 12px;
  align-items: start;
  font: 400 14px/1.55 var(--font-ui);
  color: var(--ink-3);
  max-width: 34ch;
}
.i4-std__ico {
  display: block;
  width: 22px; height: 22px;
  margin-top: 1px;                       /* optical: sits on the first baseline */
  color: var(--navy-300);
}
.i4-std__ico svg { display: block; width: 100%; height: 100%; }
/* At five columns the cells are ~230px and a 34ch gloss wraps to seven or more
   lines. Cap the docket at three tracks from 1180 and two below it, so the
   glyph column always has a paragraph worth sitting beside. */
@media (min-width: 1180px) { .i4-std__set { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 768px) and (max-width: 1179px) { .i4-std__set { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Not optional and not shrinkable (§A9): a band headed "The rules you report
   against" listing ASC 958 and Form 990 is one short step from reading as a
   claim of licensure, and this sentence is what makes it a reference strip. */
.i4-std__note { margin-top: var(--s-8); font: var(--t-cap); font-size: 12px; color: var(--ink-3); max-width: 72ch; }

/* --- single, band 8: nearby sectors (§A11, CONTAINED) -------------------
   Was three abutting full-bleed photo panels with the copy reversed out over
   a scrim. The client has banned edge-to-edge photo rows on interior pages,
   so the band is now a CONTAINED CARD ROW inside .container: three cards,
   three across at 1180, two from 768, stacked below, on the same hairline-
   on-tinted-panel construction .io__facts uses.

   The photograph is the card's head at 3:2 and it keeps a navy scrim at its
   BOTTOM EDGE ONLY - a tone that gives eleven differently-lit frames one
   common foot, not a legibility device, because the name and the teaser have
   left the photograph and now set as real text on the card's own ground where
   they are ink on paper at full contrast.

   Equal heights come from the grid, not from a fixed panel height: the body
   is a flex column and the arrow is pushed to the foot, so a two-line teaser
   and a one-line teaser end on the same baseline. */
.indrel { padding-block: var(--section-y); }
.indrel--paper { background: var(--paper); }
.indrel--wash  { background: var(--wash); }

.indrel__strip {
  margin-top: var(--s-8);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-5);
  isolation: isolate;
}
@media (min-width: 768px)  { .indrel__strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1180px) { .indrel__strip { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-6); } }

.indrel__panel {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: clip;
  text-decoration: none;
  background: var(--navy-050);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  transition: border-color var(--dur-2) var(--ease-out);
}
/* the media head: a real box in flow now, never an absolutely filled panel */
.indrel__panel picture {
  position: relative;
  display: block;
  aspect-ratio: 3 / 2;
  overflow: clip;
  background: var(--navy-050);
}
.indrel__panel img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: var(--po, 50% 42%);
  transition: transform var(--dur-5) var(--ease-soft);
}
/* the duotone foot: the bottom third of the MEDIA box, nothing else */
.indrel__panel picture::after {
  content: "";
  position: absolute; inset: auto 0 0 0;
  height: 46%;
  z-index: var(--z-overlay);
  background: linear-gradient(180deg, rgba(1, 30, 61, 0) 0, rgba(1, 30, 61, .38) 100%);
}
/* the gold edge, a state and never visible at rest, at the card's own top */
.indrel__panel::after {
  content: "";
  position: absolute; left: 0; right: 0; top: 0;
  z-index: var(--z-content);
  height: 3px;
  background: var(--gold);
  opacity: 0;
  transform: scaleX(0); transform-origin: left;
  transition: opacity var(--dur-1) var(--ease-out), transform var(--dur-2) var(--ease-out);
}
.indrel__panel:hover::after, .indrel__panel:focus-visible::after { opacity: 1; transform: scaleX(1); }
.indrel__panel:hover img,   .indrel__panel:focus-visible img { transform: scale(1.04); }
.indrel__panel:hover { border-color: var(--navy-300); }
.indrel__panel:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; }

.indrel__body {
  position: relative;
  z-index: var(--z-glass);
  flex: 1 1 auto;
  display: flex; flex-direction: column;
  gap: var(--s-2);
  padding: var(--s-5) var(--s-5) var(--s-4);
}
@media (min-width: 1180px) { .indrel__body { padding: var(--s-6) var(--s-6) var(--s-5); } }
/* ink on the card's own ground, no longer white on a photograph */
.indrel__name { font: 600 20px/1.26 var(--font-display); letter-spacing: -.016em; color: var(--navy); }
@media (min-width: 1180px) { .indrel__name { font-size: 22px; } }
.indrel__teaser { font: 400 14px/1.5 var(--font-ui); color: var(--ink-2); max-width: 40ch; }
/* SPEC-SI-R3 §5.1 item 7: no -webkit-line-clamp. The eleven map teasers are
   51 to 66 characters and every one of them clipped when there was one, so
   the card is sized by its content and the grid equalises the row instead. */
.indrel__go {
  display: block;
  margin-top: auto;
  padding-top: var(--s-4);
  color: var(--navy-300);
  transition: color var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out);
}
.indrel__go svg { width: 16px; height: 16px; display: block; }
.indrel__panel:hover .indrel__go,
.indrel__panel:focus-visible .indrel__go { color: var(--navy); transform: translateX(3px); }

/* A related sector with no photo key (§A11): the masked navy grid at the same
   3:2, in the media head's place. Never a broken image. */
/* The BASE rule is unchanged, because the case-studies hub borrows this span
   to fill a .hubplate__media that has no photograph: absolutely positioned,
   the masked grid as its own background. Only the card head overrides it. */
.indrel__pat {
  position: absolute; inset: 0;
  z-index: var(--z-media);
  background-image: var(--pat-grid-inv);
  -webkit-mask-image: radial-gradient(90% 80% at 70% 20%, #000 0%, #000 44%, transparent 80%);
          mask-image: radial-gradient(90% 80% at 70% 20%, #000 0%, #000 44%, transparent 80%);
}
.indrel__panel--flat .indrel__pat {
  position: relative;
  inset: auto;
  display: block;
  aspect-ratio: 3 / 2;
  overflow: clip;
  background-color: var(--navy);
}

/* --- single: the rail's anchor targets (§A4.4) --------------------------
   Each of the four cells lands on one of these ids, so each has to clear the
   sticky header or the band's eyebrow arrives under it. */
.page-industry :is(#ind-pain, #ind-work, #ind-std, #ind-faq) { scroll-margin-top: calc(var(--header-h) + 24px); }

/* =========================================================================
   THE HUB'S INDEX  (SPEC-I-R2 §B4, §B5)
   Eleven ruled rows and one large plate that follows the row the reader is
   pointing at. Eleven identical tiles at exactly equal weight were not a
   hierarchy, they were a contact sheet; the photography is not lost here, it
   is concentrated.
   ========================================================================= */
.indx { padding-block: var(--section-y); background: var(--wash); }
/* the rail's scroll cue lands here, so it has to clear the sticky header */
.page-industries #ind-index { scroll-margin-top: calc(var(--header-h) + 24px); }
.indx__grid { display: grid; }

/* The plate is sticky at >=1180 ONLY, and it is not rendered at all below it,
   so no phone or tablet fetches eleven photographs for a figure it cannot see.
   SPEC-00 §6.4 rations sticky: this is the site's second sticky element after
   the header, and it is what makes the follow-photo readable at all. */
@media (max-width: 1179px) { .indx__fig { display: none; } }
@media (min-width: 1180px) {
  .indx__grid { grid-template-columns: 520px minmax(0, 1fr); column-gap: var(--s-11); align-items: start; }
  .indx__fig {
    position: sticky;
    top: calc(var(--header-h) + var(--s-7));
    aspect-ratio: 4 / 5;
    border-radius: var(--r-md);
    overflow: clip;
    background: var(--navy-900);
  }
}
.indx__slide {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-soft);
}
.indx__slide.is-on { opacity: 1; }
.indx__slide picture { position: absolute; inset: 0; display: block; }
.indx__slide img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: var(--po, 50% 42%);
  transform: scale(1.04);
  transition: transform var(--dur-5) var(--ease-soft);
}
.indx__slide.is-on img { transform: none; }

/* --- the rows ----------------------------------------------------------
   Unequal heights are the point: eleven rows of unequal height read as an
   index, eleven tiles of identical height read as a grid. No lift, no shadow,
   no border-colour change; the fill, the gold edge and the solid numeral carry
   every state and all three are gated by opacity or colour, never transform. */
.indx__list { border-top: 1px solid var(--line); position: relative; }
.indx__list::before {                    /* the 56/72 x 3 gold mark on the top rule */
  content: "";
  position: absolute; left: 0; top: -1px;
  width: 56px; height: 3px;
  background: var(--gold);
  transform: scaleX(0); transform-origin: left;
  animation: grow-x 520ms var(--ease-out) both;
}
@media (min-width: 1024px) { .indx__list::before { width: 72px; } }
.indx__item { border-bottom: 1px solid var(--line); }
.indx__link {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  column-gap: var(--s-4);
  align-items: start;
  padding: 20px 16px;
  margin-inline: -16px;
  text-decoration: none;
}
.indx__link::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -1;
  background: var(--navy-050);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}
.indx__link::after {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  z-index: -1;
  width: 3px;
  background: var(--gold);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}
.indx__link:hover::before, .indx__link:focus-visible::before,
.indx__link:hover::after,  .indx__link:focus-visible::after { opacity: 1; }
.indx__link:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; border-radius: var(--r-sm); }

.indx__num {
  grid-column: 2; grid-row: 1;
  font: 600 28px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.03em;
  color: rgba(2, 45, 90, .16);           /* aria-hidden, never the only carrier */
  transition: color var(--dur-2) var(--ease-out);
}
.indx__thumb {
  grid-column: 1; grid-row: 1 / span 3;
  position: relative;
  display: block;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border-radius: var(--r-xs);
  background: var(--navy-900);
}
.indx__thumb picture { position: absolute; inset: 0; display: block; }
.indx__thumb img { width: 100%; height: 100%; object-fit: cover; object-position: var(--po, 50% 42%); }
.indx__pat {
  position: absolute; inset: 0;
  background: var(--navy) var(--pat-grid-inv);
}
.indx__body { grid-column: 2; grid-row: 2; display: block; margin-top: var(--s-2); }
.indx__name { display: block; font: 600 20px/1.24 var(--font-display); letter-spacing: -.016em; color: var(--navy); }
.indx__teaser { display: block; margin-top: 4px; font: 400 14px/1.55 var(--font-ui); color: var(--ink-2); max-width: 46ch; }
.indx__go {
  grid-column: 2; grid-row: 1;
  justify-self: end; align-self: center;
  display: block;
  color: var(--ink-3);
  transition: color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.indx__go svg { width: 18px; height: 18px; display: block; }
.indx__link:hover .indx__go, .indx__link:focus-visible .indx__go { color: var(--navy); transform: translateX(4px); }
.indx__link:hover .indx__num, .indx__link:focus-visible .indx__num { color: var(--navy); }

/* The thumbnail at <=1179 is not a degradation, it is the other half of the
   same decision: above 1180 one photograph answers the pointer, below it there
   is no pointer and no room for a plate, so every row carries its own frame.
   The two never co-exist and neither width is left without photography. */
@media (min-width: 768px) {
  .indx__link { grid-template-columns: 52px 112px minmax(0, 1fr) 18px; column-gap: var(--s-4); padding-block: 20px; }
  .indx__num   { grid-column: 1; grid-row: 1 / span 2; font-size: 32px; }
  .indx__thumb { grid-column: 2; grid-row: 1 / span 2; }
  .indx__body  { grid-column: 3; grid-row: 1 / span 2; margin-top: 0; }
  .indx__name  { font-size: 21px; }
  .indx__go    { grid-column: 4; grid-row: 1 / span 2; justify-self: start; }
}
@media (min-width: 1024px) {
  .indx__link { grid-template-columns: 56px 128px minmax(0, 1fr) 18px; column-gap: var(--s-5); padding-block: 22px; }
  .indx__num  { font-size: 34px; }
  .indx__name { font-size: 22px; }
}
@media (min-width: 1180px) {
  .indx__link  { grid-template-columns: 72px minmax(0, 1fr) 20px; column-gap: var(--s-6); padding-block: 24px; }
  .indx__thumb { display: none; }
  .indx__num   { font-size: 44px; }
  .indx__body  { grid-column: 2; }
  .indx__name  { font-size: 26px; letter-spacing: -.016em; }
  .indx__teaser { font-size: 15px; }
  .indx__go    { grid-column: 3; }
  .indx__go svg { width: 20px; height: 20px; }
  /* A persistently highlighted row makes no sense where there is no figure to
     drive it, so .is-on styling is 1180-and-up only. :hover and :focus-visible
     apply at every width. */
  .indx__link.is-on::before, .indx__link.is-on::after { opacity: 1; }
  .indx__link.is-on .indx__num { color: var(--navy); }
}

/* --- the foot (§B5) ----------------------------------------------------
   "Don't see your industry?" stops being a twelfth tile: no card, no
   photograph, no glass, and not an <li> inside the index. */
.indx__foot { margin-top: var(--s-10); border-top: 1px solid var(--line); }
.indx__foot > div {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: var(--s-8);
  padding-block: var(--s-7);
  border-bottom: 1px solid var(--line-2);
}
.indx__foot > div:last-child { border-bottom: 0; }
@media (max-width: 767px) { .indx__foot > div { grid-template-columns: minmax(0, 1fr); row-gap: var(--s-3); } }
.indx__foot-h { font: var(--t-d3); letter-spacing: -.014em; color: var(--navy); }
.indx__foot-t { font: var(--t-body); color: var(--ink-3); max-width: 46ch; }
.indx__foot .link-arrow { margin-top: 0; }

/* --- no-JS and reduced motion, industries (§A13.3, §B7) -----------------
   .ihero, .svc-cta and every .r reveal are already covered by the blocks at
   the foot of this file and of next.css. These are the states this pass adds
   that REST at a non-final value: neutralising the transition without forcing
   them leaves the gold mark undrawn and the plate blank. */
html:not(.js) .indx__list::before { transform: scaleX(1); animation: none; }
@media (prefers-reduced-motion: reduce) {
  .indx__list::before { transform: none !important; animation: none !important; }
  .indx__slide img, .indrel__panel img { transform: none !important; }
  /* .indrel__panel::after is the one gold edge on either template gated by a
     transform rather than by opacity alone, so it is given both: with
     transform neutralised the resting opacity: 0 is what keeps it invisible. */
  .indrel__panel::after { transform: none !important; }
  .indx__link, .indx__go, .indrel__panel, .indrel__go { transition: none; }
  .indx__link:hover .indx__go, .indrel__panel:hover img { transform: none !important; }
  .indrel__panel:hover .indrel__go, .indrel__panel:focus-visible .indrel__go { transform: none !important; }
}
@media (prefers-contrast: more) {
  /* the card is ink on a tinted ground now, so the contrast fix is the card's
     edge and its arrow, not the photograph's scrim */
  .indrel__panel { border-color: var(--navy-300); }
  .indrel__go { color: var(--navy); }
  .indx__num { color: rgba(2, 45, 90, .34); }
}


/* =========================================================================
   BLOG, ROUND 2  (SPEC-B-R2)
   Part A: the insights archive, 22 pages plus the term views.
   Part B: the post single, 216 URLs at the site root.

   Both templates now open on the SAME interior navy field the service single
   opens on, and close on the mirrored one. The close is .svc-cta reused
   verbatim from the SERVICE SINGLE block above: same component, same class
   names, no second copy and no second set of numbers to keep in step.

   SPEC-B-R2 §C step 3 asked for the two hero blocks in assets/next.css. They
   ship here instead. next.css is the homepage's stylesheet; both blog
   templates already load pages.css for .ihero, .legal__body and .lg-toc, so
   a two-template block in next.css would be downloaded by the homepage for
   nothing. Nothing below is shared with home-next.php.

   Nothing below renders a post cover at any size. §A2: 193 of 216 covers bake
   the post's own title into the pixels and the 2025 batch bakes in a phone
   number the firm does not use, so a heavy crop still shows fragments of type
   and a cropped letterform reads as a rendering fault. Both templates carry
   two structural photographs the firm controls instead: the hero plane and
   the closing plane. Content images inside a post are the post's own and are
   unaffected.
   ========================================================================= */

/* PRE-WIRED SWITCH (SPEC-B-R2 §B2). DO NOT BUILD until the cover generator
   ships a TEXT-FREE variant. On that day the post hero plate's <picture> swaps
   to wp_get_attachment_image() and nothing else on the template changes: the
   plate box, its radius, its drop and the 'plate-hero' sizes slot all stay as
   they are.
.ihero--post .ihero__plate--cover img { object-position: 50% 30%; }
*/

/* =========================================================================
   PART A | THE INSIGHTS ARCHIVE  (SPEC-B-R2 §A)
   The firm's ledger of what it has written. One white sequence between two
   navy ends: a lead entry on the full measure, then numbered ruled rows with
   the particulars in a right-hand rail, then pagination on a hairline.
   ========================================================================= */

/* Bands 2 and 3 are ONE continuous --paper sequence, so band 3 carries no
   padding-top of its own and the lead's own top rule does the break. */
.insa { padding-top: var(--s-9); padding-bottom: var(--section-y); background: var(--paper); }
.insa__none { font: var(--t-lead); color: var(--ink-2); max-width: 52ch; }
.insa__none + .link-arrow { margin-top: var(--s-5); }

/* --- band 2: the lead (§A4.3) -------------------------------------------
   Renders on page 1 of a non-search view only. The measured defect was 766px
   of dead space to the right of a headline capped at 530 inside a 1296
   measure. The particulars move into a 208px right cell and the remainder
   becomes air inside the headline's OWN cell, which is what a lead is allowed
   to have and a row is not. */
.insa-lead {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  padding-block: var(--s-7) var(--s-9);
  border-top: 2px solid var(--navy-100);
}
.insa-lead__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-3);
  font: var(--t-cap); color: var(--ink-3);
}
.insa-lead__cat {
  font: 600 11px/1.4 var(--font-display);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--navy);
}
.insa-lead__sep {
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--navy-300);
}
.insa-lead__h {
  margin-top: 0; font: var(--t-d2); letter-spacing: -.022em;
  text-wrap: balance; overflow-wrap: anywhere;
}
/* Below the split the particulars are an inline row ABOVE the headline, which
   is where a dateline belongs on a stacked entry. `order` does it, so the DOM
   keeps title, dek, particulars and the accessible order is unchanged. */
@media (max-width: 1023px) {
  .insa-lead__meta { order: -1; margin-bottom: var(--s-5); }
}
.insa-lead__link { color: var(--ink); text-decoration: none; transition: color var(--dur-1) var(--ease-out); }
/* whole-block target. The meta and the dek are OUTSIDE the anchor, so the
   accessible name is the post title alone. */
.insa-lead__link::after { content: ""; position: absolute; inset: 0; }
.insa-lead:hover .insa-lead__link,
.insa-lead:focus-within .insa-lead__link {
  color: var(--navy-500); text-decoration: underline;
  text-underline-offset: 5px; text-decoration-thickness: 2px;
}
.insa-lead__dek { margin-top: var(--s-5); font: var(--t-lead); color: var(--ink-3); }
/* §A4.3: a <span>, never an <a>. It sits INSIDE the stretched-link block, so
   as an anchor it would be a second tab stop with the same destination. It is
   decorative repetition and it is aria-hidden. */
.insa-lead__go { margin-top: var(--s-6); }
.insa-lead:hover .insa-lead__go { color: var(--navy-500); }
.insa-lead:hover .insa-lead__go svg { transform: translateX(4px); }
@supports selector(:has(*)) {
  .insa-lead:has(.insa-lead__link:focus-visible) {
    outline: 2px solid var(--navy); outline-offset: 8px; border-radius: var(--r-xs);
  }
  .insa-lead .insa-lead__link:focus-visible { outline: none; }
}
/* The headline caps are PX, not ch. §A6 states them as "18ch (~594px at
   42px)", which is 33px per ch: that is the font SIZE, not the advance of
   Poppins' "0", which is closer to 0.55em. A literal 18ch would cap this
   headline at ~414px and run the longest title in the corpus to six lines.
   The stated MEASURE is the binding number, exactly as the .lg-doc__grid
   block above already records for .legal__body. */
@media (min-width: 768px) {
  .insa-lead__h   { max-width: 620px; }
  .insa-lead__dek { max-width: 60ch; }
  .insa-lead { padding-block: var(--s-8) var(--s-11); }
}
@media (min-width: 1024px) {
  .insa-lead {
    grid-template-columns: minmax(0, 1fr) 180px;
    column-gap: var(--s-9);
    align-items: start;
    padding-block: var(--s-9) var(--s-10);
  }
  /* DOM order is body then meta, so the meta is placed back into row 1 of the
     right track rather than moved in the source: the accessible order stays
     title, dek, particulars. */
  .insa-lead__body { grid-column: 1; grid-row: 1; }
  .insa-lead__meta {
    grid-column: 2; grid-row: 1;
    flex-direction: column; align-items: flex-end;
    gap: var(--s-2);
    text-align: right;
    padding-top: 4px;                  /* optical: --t-cap's cap height sits above --t-d2's */
  }
  .insa-lead__sep { display: none; }   /* stacked, so the dots have nothing to separate */
  .insa-lead__h   { max-width: 594px; }
}
@media (min-width: 1180px) {
  .insa-lead {
    grid-template-columns: minmax(0, 1fr) 208px;
    column-gap: var(--s-10);
    padding-block: var(--s-9) var(--s-11);
  }
  .insa-lead__dek { max-width: 56ch; }
}

/* --- band 3: the rows (§A4.4) --------------------------------------------
   The archive row IS the homepage journal row: .ins-row, .ins-row__num,
   .ins-row__title, .ins-row__link, .ins-row__chip, the ::before hover bar and
   the :has() focus ring all come from next.css untouched, which is what makes
   the homepage, this page and the post's related band one component. The
   archive adds exactly two things: a right meta rail and a dek. Nothing here
   restyles a shared class. */
.insa__list { margin-top: 0; }

/* THE FIFTH RULE. Ten identical hairlines is a list; a ledger rules every
   fifth line, and this is an accounting firm. One selector, no markup, no
   asset, and it is the only ornament on the band. */
.insa-row:nth-child(5n+1) { border-top-width: 2px; border-top-color: var(--navy-100); }

.insa-row__dek {
  margin-top: var(--s-3);
  font: var(--t-body-sm);
  color: var(--ink-3);
}
/* The rail is .ins-row__meta with a second class, so its type, its colours and
   its dot separators are the component's and only its PLACEMENT changes. */
.insa-row__rail { margin-bottom: var(--s-2); }

@media (max-width: 767px) {
  /* 36px numeral track, and the meta is an inline row above the title. The
     whole row is the target and it is 172px tall here, so no arrow chip. */
  .insa-row { grid-template-columns: 36px minmax(0, 1fr); padding-block: var(--s-6); }
  .insa-row > .ins-row__num  { grid-column: 1; grid-row: 1; }
  .insa-row > .insa-row__rail { grid-column: 2; grid-row: 1; }
  .insa-row > .ins-row__text { grid-column: 2; grid-row: 2; }
}
@media (min-width: 768px) {
  .insa-row {
    grid-template-columns: 60px minmax(0, 1fr) 160px;
    column-gap: var(--s-5);
    align-items: start;
    padding-block: var(--s-7);
  }
  .insa-row > .ins-row__chip { display: none; }   /* three tracks, no fourth */
  .insa-row__rail {
    grid-column: 3;
    flex-direction: column; align-items: flex-end;
    gap: var(--s-2);
    text-align: right;
    margin-bottom: 0;
    padding-top: 4px;
  }
  .insa-row__rail .ins-row__sep { display: none; }
  .insa-row .ins-row__title { margin-top: 0; }
  .insa-row__dek { max-width: 60ch; }
}
@media (min-width: 1024px) {
  .insa-row {
    grid-template-columns: 72px minmax(0, 1fr) 180px 44px;
    column-gap: var(--s-6);
    padding-block: var(--s-7);
  }
  .insa-row > .ins-row__chip { display: inline-flex; }
  .insa-row .ins-row__num { font-size: 20px; }
  /* §A6's "20ch (~600px)", read as the measure and not as the unit: see the
     .insa-lead__h note above. */
  .insa-row .ins-row__title { max-width: 600px; }
  .insa-row__dek { max-width: 62ch; }
}
@media (min-width: 1180px) {
  .insa-row {
    grid-template-columns: 88px minmax(0, 1fr) 208px 44px;
    column-gap: var(--s-7);
  }
  .insa-row .ins-row__num { font: var(--t-num-sm); font-variant-numeric: tabular-nums; }
}

/* PRE-WIRED SWITCH (§A2). DO NOT BUILD until the cover generator ships a
   TEXT-FREE cover variant. On that day the plate becomes a fixed-ratio cell
   in the row grid and the PHP branch is eight lines: add a 132px track to the
   four rules above and render the <picture> as the row's second child.
   Today a 208px cover column is nine thumbnails of the same stock office with
   the reader's own headline printed across them, which is why it is a comment.
.insa-row__plate {
  grid-column: 2; align-self: start;
  aspect-ratio: 3 / 2; overflow: hidden;
  border-radius: var(--r-sm); background: var(--navy-050);
}
.insa-row__plate img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 42%; }
*/

/* --- band 3b: pagination (§A4.5) -----------------------------------------
   Machinery unchanged: paginate_links() builds every href and the template
   only rewrites classes. This is a restyle, so it stops reading as a row of
   ten buttons. The numerals lose their boxes; Previous and Next keep theirs,
   because they are the only two controls that deserve one. */
.insa-pg {
  margin-top: var(--s-10);
  padding-top: var(--s-6);
  border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: var(--s-4);
}
.insa-pg__count { font: var(--t-cap); color: var(--ink-3); }
.insa-pg__list {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-1);
  list-style: none;
}
.insa-pg__link {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px; padding-inline: var(--s-3);
  border-radius: var(--r-xs);
  font: var(--t-label); font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
  color: var(--ink-2); text-decoration: none;
  transition: background var(--dur-1) var(--ease-out),
              color      var(--dur-1) var(--ease-out);
}
.insa-pg__link:hover { background: var(--navy-050); color: var(--navy); }
.insa-pg__link.is-on { background: var(--navy); color: #fff; }
.insa-pg__link--prev,
.insa-pg__link--next {
  padding-inline: var(--s-4);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  transition: background var(--dur-1) var(--ease-out),
              color      var(--dur-1) var(--ease-out),
              border-color var(--dur-1) var(--ease-out);
}
.insa-pg__link--prev:hover,
.insa-pg__link--next:hover { border-color: var(--navy); }
.insa-pg__dots {
  display: inline-flex; align-items: center; min-height: 44px;
  padding-inline: var(--s-1); color: var(--navy-300);
}
@media (min-width: 768px) {
  .insa-pg { flex-direction: row; justify-content: space-between; align-items: center; }
  .insa-pg__list { justify-content: flex-end; }
}

/* =========================================================================
   PART B | THE POST SINGLE  (SPEC-B-R2 §B)
   The article becomes a document with a rail, not a column with a hole beside
   it. The reading column widens to a real measure; the left rail becomes a
   sticky, permanently occupied spine holding the contents, the share links and
   the byline; figures, tables and embeds break out to the right into the
   360px that used to be dead.
   ========================================================================= */

/* --- band 2: the document (§B4.3) ----------------------------------------
   TWO LEFT EDGES, FIXED BY RULE, AND IT MUST STAY FIXED: everything on this
   page hangs off ONE left edge, the container gutter. The breadcrumb, the
   kicker, the h1, the standfirst, the meta row, the rail, the related head
   and the pagination all start there. The PROSE is the single inset element
   and its inset is exactly the rail plus the gap. Do not re-centre this grid.

   Same ground as the hero, so SPEC-00 §2.3 applies: padding-top is zero and a
   64px separator over a --line-2 hairline does the break instead. */
.post-doc { padding-top: 0; padding-bottom: var(--section-y); background: var(--paper); }
.post-doc__grid {
  display: grid;
  gap: var(--s-8);
  padding-top: var(--s-9);
  border-top: 1px solid var(--line-2);
}
@media (min-width: 768px) {
  .post-doc__grid { padding-top: var(--s-10); }
}
/* Below the two-column split every block is capped at the reading measure and
   centred in the container. Without this the prose runs the full 959px measure
   at 1023 and sets ~94 characters to the line. `justify-content: center` alone
   does not do it: a single auto track still resolves to the container width
   because a paragraph's max-content is the whole paragraph.

   673px, not 66ch: `ch` on the contents card and on .post-adj would resolve
   against THEIR font sizes (16 and 15px) and land 70 to 110px narrower than
   the 17px prose, so the five blocks would not share a left edge. 673 is
   SPEC-B-R2 §B4.3's own measured 66ch at 17px Mulish. */
@media (max-width: 1023px) {
  .post-rail__toc,
  .post-rail__share,
  .post-by,
  .post-body,
  .post-adj { width: 100%; max-width: 673px; margin-inline: auto; }
}
@media (min-width: 1024px) {
  .post-doc__grid {
    /* The prose track is stated in PX, not ch. A `ch` on the grid container
       resolves against the CONTAINER's font (16px, inherited from body), not
       against .post-body's 18px, so `64ch` here would cap the column at ~580
       and silently undo override 7. These are §B4.3's own measured numbers:
       64ch at 18px Mulish is 691 and 66ch is 713. .post-body keeps its own
       66ch cap as well, where the unit does resolve correctly. */
    grid-template-columns: 216px minmax(0, 691px);
    /* two explicit rows, so the rail can span BOTH with `grid-row: 1 / -1`.
       Without them -1 resolves to line 1 of an empty explicit row set and the
       rail silently collapses to one row. row-gap is 0 and .post-adj carries
       its own 64: an absent prev/next then costs no trailing space. */
    grid-template-rows: auto auto;
    column-gap: var(--s-9);
    row-gap: 0;
    align-items: start;
    justify-content: start;
  }
}
@media (min-width: 1180px) {
  .post-doc__grid {
    grid-template-columns: 240px minmax(0, 713px);
    column-gap: var(--s-10);
  }
}

/* --- the rail (§B4.4) ----------------------------------------------------
   ONE sticky block containing three groups, so the rail is never empty and
   never orphans anything. This is the fix for the measured defect: .post-by
   sat at x=65 because it was auto-placed into the TOC track roughly 900px
   below the end of the list, which is a bug that looked like a design.
   Putting it there ON PURPOSE, as the rail's last group, converts an accident
   into a composition and fills the rail's lower half on every post.

   At <=1179 nothing is sticky, so a 200% zoom user does not lose the viewport
   to a fixed column. */
/* display: contents on BOTH wrappers below 1024, so the three groups become
   grid items in the single column and `order` can put the prose between them
   without moving anything in the DOM. DOM order stays contents, share,
   byline, prose, prev/next, which is the tab order §B9 specifies. */
.post-rail,
.post-rail__in { display: contents; }
.post-rail__h {
  font: var(--t-eyebrow);
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-3);
}

/* the contents group. At 768 it goes two columns, measuring ~196px against
   the 424px wall the reader used to meet; at 375 it compresses to 15px on
   44px rows over --wash, so it reads as a contents card the eye can skip
   rather than as the article failing to start. */
.post-toc__list { margin-top: var(--s-4); counter-reset: ptoc; list-style: none; }
.post-toc__list li { counter-increment: ptoc; break-inside: avoid; }
.post-toc__list a {
  display: flex; gap: var(--s-3); align-items: baseline;
  min-height: 44px; padding: var(--s-2) 0;
  font: 400 15px/1.45 var(--font-ui);
  color: var(--ink-2);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease-out);
}
.post-toc__list a::before {
  content: counter(ptoc, decimal-leading-zero);
  flex: 0 0 auto; width: 22px;
  font: 600 12px/1.4 var(--font-display);
  font-variant-numeric: tabular-nums;
  color: var(--navy-300);
  transition: color var(--dur-1) var(--ease-out);
}
.post-toc__list a:hover,
.post-toc__list a:hover::before { color: var(--navy); }

/* the share group. Four links, no library, no script, no tracking pixel. Each
   is a 44px row with the platform's own glyph and a VISIBLE TEXT LABEL: an
   icon-only share row is four unlabelled 16px targets. No copy-link button,
   which would need JavaScript SPEC-00 §8.4 does not permit. */
.post-share__list { margin-top: var(--s-3); list-style: none; }
.post-share__list a {
  display: flex; align-items: center; gap: var(--s-3);
  min-height: 44px;
  font: 600 14px/1.4 var(--font-ui);
  color: var(--ink-2);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease-out);
}
.post-share__list a svg { flex: 0 0 auto; width: 16px; height: 16px; color: var(--ink-3); transition: color var(--dur-1) var(--ease-out); }
.post-share__list a:hover,
.post-share__list a:hover svg { color: var(--navy); }

/* the byline group. The Organization, and no person is named: all 216 posts
   carry post_author = 4 whose user_login is `it-finlert`, so get_the_author()
   is never called. No avatar, no bio, no invented author. */
.post-by { max-width: none; margin-top: 0; padding-top: 0; border-top: 0; }
.post-by__h    { font: var(--t-d4); letter-spacing: -.010em; color: var(--navy); }
.post-by__text { margin-top: var(--s-3); font: var(--t-body-sm); color: var(--ink-3); }
.post-by .link-arrow { margin-top: var(--s-4); }

@media (max-width: 1023px) {
  /* unstacked into separate blocks in the single column, in reading order:
     contents above the prose, then the prose, then share, then byline, then
     prev/next. The contents block reads as a card the eye can skip. */
  .post-rail__toc   { order: 1; }
  .post-body        { order: 2; }
  .post-rail__share { order: 3; }
  .post-by          { order: 4; }
  .post-adj         { order: 5; }
  .post-rail__toc {
    padding: var(--s-5);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--wash);
  }
  .post-rail__share,
  .post-by { padding-top: var(--s-6); border-top: 1px solid var(--line); }
  .post-share__list { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-6); }
}
@media (min-width: 768px) and (max-width: 1023px) {
  .post-toc__list { columns: 2; column-gap: var(--s-8); }
}
@media (max-width: 767px) {
  .post-toc__list a { min-height: 44px; padding-block: var(--s-2); }
}
@media (min-width: 1024px) {
  /* one sticky block. The three groups travel together, so the rail is never
     half empty and the byline is never orphaned 900px below the list. */
  .post-rail {
    display: block;
    grid-column: 1; grid-row: 1 / -1;
    align-self: stretch;               /* sticky needs a tall containing block */
  }
  .post-rail__in {
    display: block;
    position: sticky;
    top: calc(var(--header-h) + var(--s-6));
    z-index: var(--z-rail);
    max-height: calc(100vh - var(--header-h) - var(--s-10));
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .post-rail__in > * + * {
    margin-top: var(--s-6);
    padding-top: var(--s-6);
    border-top: 1px solid var(--line);
  }
  .post-body { grid-column: 2; grid-row: 1; }
  .post-adj  { grid-column: 2; grid-row: 2; margin-top: var(--s-10); }
}

/* --- the reading surface (§B4.5) -----------------------------------------
   Still .legal__body, COMPOSED rather than copied: SPEC-00 §10 forbids a
   parallel long-form component and .legal__body is the site's one. It carried
   six overrides; it now carries nine, and the three new ones are measure and
   rhythm, not new components.

   THE SANITISER IS UNTOUCHED. Every step of fa_post_body() keeps its order and
   its behaviour. Nothing on this page requires a change to it. */

/* 0. .legal__body caps every <p> at 60ch on top of its own container cap,
      which is right for a legal document laid out at 612px and wrong here:
      the measure belongs to the column, once, and override 7 sets it. */
.post-body p { max-width: none; }
/* p + p is 24, not .legal__body's 20: this is 6,000px of reading, not eight
   clauses. .legal__body's own `:is(h2,h3,h4) + *` rule is more specific and
   still supplies the 16px a heading takes. */
.post-body > * + * { margin-top: var(--s-6); }

/* 1. .legal__body renders its first paragraph as an effective-date standfirst.
      On an article the first paragraph is the opening, so it steps UP. */
.post-body > p:first-of-type {
  margin-bottom: 0; padding-bottom: 0; border-bottom: 0;
  font: 400 20px/1.6 var(--font-ui); color: var(--ink-2); max-width: 52ch;
}
/* 2. Media breaks the measure. Text stays at 66ch and a figure, an embed or a
      table takes 905px, which is the whole reason the grid is wider than the
      prose. 127 of 216 posts contain an image and they used to stop 360px
      short of the container edge. The breakout is defined at >=1180 only;
      below that the media is simply the column. */
.post-body :is(figure, .post-embed, .legal-table-wrap, hr) { max-width: 100%; }
/* 3. Figures. */
.post-body figure { display: block; margin-block: var(--s-9); }
.post-body figure img { width: 100%; height: auto; border-radius: var(--r-md); display: block; }
.post-body figcaption {
  margin-top: var(--s-3); font: var(--t-cap); color: var(--ink-3); max-width: 60ch;
  padding-left: var(--s-4); border-left: 2px solid var(--navy-100);
}
/* 4. Bare images the legacy corpus emits without a <figure>. The --wash ground
      holds the box while a lazy image decodes, so the reflow is a fill rather
      than a jump where the dimension backfill could not resolve one. */
.post-body > p > img,
.post-body > img {
  display: block; margin-block: var(--s-8); border-radius: var(--r-md); background: var(--wash);
}
/* 5. Embeds. NO iframe is ever rendered without this box: a legacy iframe
      carries width="560" and overflows a 335px column at 375. */
.post-embed {
  position: relative; margin-block: var(--s-9); aspect-ratio: 16 / 9;
  border-radius: var(--r-md); overflow: hidden; background: var(--navy-050);
}
.post-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* 6. .legal__body's h2 rule is designed for a legal document's ~8 clauses. An
      article with 12 h2s at 64px + 20px + a rule reads as twelve chapter
      breaks, so the pitch tightens one step. The rule stays: it is what makes
      a 6,000px article scannable. */
.post-body h2 {
  margin-top: var(--s-10); padding-top: var(--s-5);
  font: 700 clamp(22px, .55vw + 17px, 30px)/1.26 var(--font-display);
  letter-spacing: -.018em;
  color: var(--ink);
}
.post-body h3 { margin-top: var(--s-8); font: 600 20px/1.35 var(--font-display); letter-spacing: -.012em; color: var(--navy); }
.post-body h4 { margin-top: var(--s-6); font: 600 17px/1.4 var(--font-ui); color: var(--navy); }
/* 7. PROSE MEASURE AND SIZE. pages.css already records that 1ch of Mulish at
      17px is 10.2px, so 66ch at 18px is ~713px and sets 66 to 70 characters to
      the line, which is the centre of the 45-75 band. The old 612px at 17px
      set 60. The step from 17 to 18px is the ONE type change on the reading
      surface and it is what makes 66ch land at a comfortable width rather
      than a wide one. */
@media (min-width: 1024px) {
  .post-body,
  .post-body :is(p, ul, ol, dl, li) { font-size: 18px; line-height: 1.72; }
  .post-body { max-width: 66ch; }
}
/* 8. Blockquote: 2px --navy-100 above AND below, no left bar. A left bar in a
      document that already indents nothing reads as a nested list. */
.post-body blockquote {
  margin-block: var(--s-9);
  padding-block: var(--s-6);
  padding-left: 0;
  border-left: 0;
  border-top: 2px solid var(--navy-100);
  border-bottom: 2px solid var(--navy-100);
  font: 400 19px/1.62 var(--font-ui);
  color: var(--ink-2);
}
.post-body blockquote :is(p, li) { font-size: 19px; line-height: 1.62; max-width: none; }
.post-body blockquote cite { font: var(--t-cap); color: var(--ink-3); }
/* 9. Tables. */
.post-body table { margin-block: var(--s-9); font: 400 16px/1.5 var(--font-ui); }
.post-body thead th {
  font: var(--t-label); text-transform: uppercase; letter-spacing: .06em;
  color: var(--navy); background: var(--navy-050);
}
.post-body :is(td, tbody th) { font: 400 16px/1.5 var(--font-ui); color: var(--ink-2); border-bottom: 1px solid var(--line-2); }

/* Heading ids are deterministic from the heading TEXT and never from an
   index, so every shared deep link survives an inserted section. The target
   has to clear the sticky header or the heading arrives under it. */
.post-body :is(h2, h3) { scroll-margin-top: calc(var(--header-h) + 24px); }

/* the breakout, at the one width where there is room for it. Measured at
   1440: the grid runs x=65 to x=1082 and a 905px figure starts at x=369 and
   ends at x=1274, 87px inside the container's right edge. */
@media (min-width: 1180px) {
  /* §B4.5 writes this as one :is() list including `> img`. A relative
     selector is not valid inside :is(), and a forgiving list would silently
     drop that one argument, so the bare legacy image gets its own rule. Same
     result, and it cannot fail quietly. */
  .post-body :is(figure, .post-embed, .legal-table-wrap, hr) { width: 905px; max-width: none; }
  .post-body > img { width: 905px; max-width: none; }
}

/* --- band 2b: prev and next (§B4.6) --------------------------------------
   Inside band 2 under the band-within-a-band rule, not a section of its own. */
.post-adj {
  display: grid; gap: var(--s-6);
  margin-top: var(--s-6);            /* + the grid's own 40px row gap = 64 */
}
.post-adj__cell {
  position: relative;
  padding-top: var(--s-5);
  border-top: 1px solid var(--line);
}
.post-adj__cell::before {
  content: "";
  position: absolute; left: 0; right: 0; top: -1px; height: 2px;
  background: var(--navy);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur-2) var(--ease-out);
}
.post-adj__cell:hover::before,
.post-adj__cell:focus-within::before { transform: scaleX(1); }
.post-adj__lbl {
  font: var(--t-eyebrow); text-transform: uppercase; letter-spacing: .14em;
  color: var(--ink-3);
}
.post-adj__t { margin-top: var(--s-3); font: 600 20px/1.32 var(--font-display); letter-spacing: -.012em; }
.post-adj__t a { color: var(--navy); text-decoration: none; }
/* one <a> per cell, the label OUTSIDE it, so each accessible name is the
   sibling's title alone */
.post-adj__t a::after { content: ""; position: absolute; inset: 0; }
.post-adj__cell:hover .post-adj__t a { color: var(--navy-500); text-decoration: underline; text-underline-offset: 4px; }
@supports selector(:has(*)) {
  .post-adj__cell:has(a:focus-visible) { outline: 2px solid var(--navy); outline-offset: 6px; border-radius: var(--r-xs); }
  .post-adj__cell a:focus-visible { outline: none; }
}
@media (min-width: 768px) {
  .post-adj { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-8); }
  /* when one side is absent the present cell spans both tracks */
  .post-adj__cell:only-child { grid-column: 1 / -1; }
  .post-adj__cell--next { text-align: right; }
  .post-adj__cell--next::before { transform-origin: right; }
}

/* --- band 3: keep reading (§B4.7) ----------------------------------------
   Three .ins-row entries in ONE ruled column at the full measure, numbered 01
   to 03: the same component the homepage journal and the archive render, so a
   hover change, a focus fix or a type step now happens once. --wash rather
   than --paper because band 2 above is --paper and SPEC-00 §2.3 would
   otherwise compress a band that should read as a clean break. .ins-row's 1px
   --line rules measure well above §8.1's 3:1 floor on --wash. */
/* SEO-QA-2026-09-20 §4 | the "Where we can help" band on the blog single.
   It sits between the article and "Keep reading", so it takes --paper and
   leaves .post-rel below it on --wash: the two bands must not read as one.
   No new component: the rows are .link-arrow, which next.css already owns. */
.post-svc { padding-block: var(--section-y); background: var(--paper); }
.post-svc__list { margin-top: var(--s-6); list-style: none; padding: 0;
  display: grid; gap: 0; }
.post-svc__item { border-top: 1px solid var(--line); }
.post-svc__item:last-child { border-bottom: 1px solid var(--line); }
.post-svc__item .link-arrow { display: flex; align-items: center;
  justify-content: space-between; gap: var(--s-4);
  padding-block: var(--s-4); width: 100%; }

.post-rel { padding-block: var(--section-y); background: var(--wash); }
.post-rel__rows { margin-top: var(--s-8); }
@media (min-width: 768px) { .post-rel__rows { margin-top: var(--s-9); } }

/* --- no-JS and reduced motion (SPEC-B-R2 §A8, §B8) -----------------------
   The hero's own reduced-motion contract is stated in full inside the
   SPEC-IH-R3 block at the top of this file. What is left here is the blog
   templates' body components. */
@media (prefers-reduced-motion: reduce) {
  .insa-row::before,
  .insa-row .ins-row__num,
  .insa-row .ins-row__chip,
  .post-adj__cell::before,
  .post-share__list a,
  .post-share__list a svg,
  .post-toc__list a { transition: none; }
}

/* =========================================================================
   CASE STUDIES, ROUND TWO  (SPEC-C-R2) - the single and the hub
   Replaces the SPEC-C1 block. A case study is a record, so both pages are
   set as records: the same navy field the rest of the interior opens on
   (.ihero .ihero--single, by reuse), the engagement facts in its index bar,
   the proof as
   one ledger on the .svc-del__panel paper panel, the narrative as numbered
   chapters whose findings are ruled rows, the client's words on .tqc,
   the .indrel__strip, and .pl-cta to close. Zero cards. Both ends navy, the
   ground alternating between them, and gold appears once per band.

   Everything reused is REUSED, not copied: .ihero*, .svc-del__panel /
   __grid / __aside (scope extended to .page-case-study above), .tqc,
   .indrel*, .hubplate*, .pl-cta*. Only the case-study deltas live here.
   ========================================================================= */


/* --- 2. what changed (§6) ------------------------------------------------
   The proof leaves navy for the .svc-del__panel paper panel inside a --wash
   band: the hero and the CTA become the page's two navy moments, the metrics
   stop sharing a colour identity with the CTA, and the numerals go UP to 48px
   full-strength navy on white. One ruled row per metric in one column at
   every count, so there is no hole and no --n. The numeral is the meaning
   here, not an index, so it is --navy and not the pale .svc-del__num. No
   hover: these rows are not links. No count-up. */
/* ROUND THREE. The three ruled rows are replaced by the facts strip: the
   .io__facts panel of SPEC-IO-R3 §4, turned through 90 degrees so the three
   readings sit ACROSS rather than down. The old shape put a 48px numeral and a
   six-word note at opposite ends of a 1296px row, three times, at 140px a row;
   the whole panel is now shorter than one of those rows was, and the three
   readings can be compared at a glance because they are side by side.
   No hover: these cells are not links. */
.csout { padding-block: var(--section-y); }
.csout--paper { background: var(--paper); }
.csout--wash  { background: var(--wash); }
.csout__h {
  margin-top: var(--s-3);
  font: var(--t-d2);
  letter-spacing: -.022em;
  color: var(--navy);
  max-width: 16ch;
  text-wrap: balance;
}
.csout__strip {
  margin: var(--s-7) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  background: var(--navy-050);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: clip;
}
.csout__cell { display: block; min-width: 0; padding: var(--s-5) var(--s-6); }
.csout__cell + .csout__cell { border-top: 1px solid var(--navy-100); }
.csout__k {
  display: block;
  font: 600 11px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: .20em;
  color: var(--ink-3);                    /* 5.4:1 on --navy-050 */
}
.csout__num {
  display: block;
  margin-top: 12px;
  font: 600 32px/1.05 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.030em;
  color: var(--navy);                     /* 13.7:1 on --navy-050 */
  overflow-wrap: anywhere;
}
.csout__note {
  display: block;
  margin-top: 8px;
  font: var(--t-body-sm);
  color: var(--ink-2);                    /* 9.0:1 */
  max-width: 32ch;
}
@media (min-width: 768px) {
  .csout__strip[data-cells="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .csout__strip[data-cells="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* across, the divider is vertical and the top rule goes away entirely */
  .csout__strip[data-cells="2"] > .csout__cell + .csout__cell,
  .csout__strip[data-cells="3"] > .csout__cell + .csout__cell {
    border-top: 0;
    border-left: 1px solid var(--navy-100);
  }
  .csout__cell { padding: var(--s-6); }
  .csout__num { font-size: 38px; }
}
@media (min-width: 1180px) {
  .csout__strip { margin-top: var(--s-8); }
  .csout__cell { padding: var(--s-7); }
  .csout__num { font-size: 44px; }
}

/* --- 3. the narrative (§7, recomposed) ----------------------------------
   ONE template rule, and it is the fix for the empty rail the critique
   caught: a COMPACT HEAD ROW (numeral and h2 on one baseline, the lead
   paragraph beside them at 7/5 only when the field has one) and then the
   findings FULL WIDTH beneath it as .svc-cov register cells. The 380px aside
   is gone: two of the three chapters had nothing to put in it, so it drew
   380px of empty paper beside a list twice on one page.

   The numeral is no longer 72px, because it is no longer holding a column on
   its own. It sits on the h2's baseline at display weight and stays pale: it
   is aria-hidden and never the only carrier of the chapter's order.

   The register cells are .svc-cov__*, reused unmodified from the service
   deliverables and the industry pressure points. Nothing about them is
   redeclared here. */
.csch { padding-block: var(--section-y); }
.csch--paper { background: var(--paper); }
.csch--wash  { background: var(--wash); }
.csch__ch { padding-block: var(--s-9); }
.csch__ch + .csch__ch { border-top: 1px solid var(--line); }
.csch__ch:first-child { padding-top: 0; }
.csch__ch:last-child  { padding-bottom: 0; }

.csch__head { display: grid; gap: var(--s-4); }
.csch__id { display: flex; align-items: baseline; gap: var(--s-4); min-width: 0; }
.csch__num {
  flex: none;
  font: 600 30px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.030em;
  color: rgba(2, 45, 90, .22);            /* aria-hidden, never the only carrier */
}
.csch__h {
  margin: 0;
  font: var(--t-d3);
  letter-spacing: -.016em;
  color: var(--ink);
  text-wrap: balance;
  scroll-margin-top: calc(var(--header-h) + var(--s-6));
}
/* the chapter's scene-setter, trailing colon and all, exactly as authored */
.csch__lead { margin: 0; font: var(--t-body-sm); color: var(--ink-3); max-width: 56ch; }
/* a prose chapter, or a mixed body's paragraphs ahead of its cells. The
   .legal__body effective-date standfirst is simply wrong on a chapter: reset,
   do not re-style. No list styling can leak in, because every list has
   already become cells. */
.csch__prose { max-width: 62ch; margin-top: var(--s-6); }
.csch__prose > p:first-of-type {
  margin-bottom: 0; padding-bottom: 0; border-bottom: 0;
  font: inherit; color: inherit; max-width: 60ch;
}
/* the register's own margin-top is authored for a band head, not a chapter
   head, so the chapter sets its own and the two never disagree */
.csch__reg { margin-top: var(--s-6); }

@media (min-width: 768px) {
  .csch__num { font-size: 34px; }
}
@media (min-width: 1024px) {
  /* the lead's column is only DRAWN when the lead exists: a chapter without
     one is a single track and its h2 is not artificially narrowed */
  .csch__head--lead {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    column-gap: var(--s-8);
    align-items: end;
  }
  .csch__num { font-size: 40px; }
  .csch__reg { margin-top: var(--s-7); }
}
@media (min-width: 1180px) { .csch__num { font-size: 44px; } }

/* --- 4. the client's words (§8) -----------------------------------------
   The wall-of-four's card with the second card replaced by a photograph.
   .tqc is next.css's SPEC-06-R3 card, untouched: paper, hairline border,
   20px radius, the quote at 18px Mulish and the author's portrait at 48px in
   the footer row. .tqc--accent gives this one the gold quotation mark, which
   is the page's whole gold allowance in this band. The figure stretches to
   the card's height at every quote length from 1024 up; 16:9 beneath the card
   at 561 to 1023; 3:2 stacked below. No shadow, no outline. */
.csq { padding-block: var(--section-y); }
.csq--paper { background: var(--paper); }
.csq--wash  { background: var(--wash); }
.csq__wall { display: grid; gap: var(--s-5); align-items: stretch; }
.csq__wall .tqc { margin: 0; }
.csq__fig {
  position: relative;
  margin: 0;
  aspect-ratio: 3 / 2;
  overflow: clip;
  border-radius: var(--r-md);
  background: var(--navy-050);
}
.csq__fig picture { position: absolute; inset: 0; display: block; }
.csq__fig img { width: 100%; height: 100%; object-fit: cover; object-position: var(--po, 50% 44%); }
@media (min-width: 561px) {
  .csq__wall { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--s-6); }
  .csq__wall .tqc { grid-column: span 6; }
  .csq__fig { grid-column: span 6; aspect-ratio: 16 / 9; border-radius: var(--r-lg); }
}
@media (min-width: 1024px) {
  .csq__wall { grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--s-5); }
  .csq__wall .tqc { grid-column: span 7; }
  .csq__fig { grid-column: span 5; aspect-ratio: auto; min-height: 320px; }
}
@media (min-width: 1180px) { .csq__wall { gap: var(--s-6); } }

/* --- 5. related (§9.1) --------------------------------------------------
   .indrel__strip by reuse, now the contained card row. --n is the card count
   set inline, so a two-card row is two half-container cards and never a
   three-track grid with a hole in it. It only ever NARROWS the row: min() is
   taken against the breakpoint's own track count so one card does not stretch
   to the full 1296px measure. */
@media (min-width: 768px) {
  .page-case-study .indrel__strip { grid-template-columns: repeat(min(var(--n, 3), 2), minmax(0, 1fr)); }
}
@media (min-width: 1180px) {
  .page-case-study .indrel__strip { grid-template-columns: repeat(min(var(--n, 3), 3), minmax(0, 1fr)); }
}

/* --- 6. the hub's rows (§10.2) ------------------------------------------
   One .csrow per study: the .hubplate (by reuse, --flip on odd indexes) then
   the ledger in .container, ground alternating on the row index. The whole
   row is one link target through the title's stretched ::after: exactly one
   <a>, no arrow, no "read the case study", no nested control. The plate hover
   is `scale`, not `transform`, because plate-scale's fill-mode owns
   `transform` on the img and would cancel a transform hover. */
.csh--paper { background: var(--paper); padding-block: var(--section-y); }
.csh__none { font: var(--t-lead); color: var(--ink-2); max-width: 52ch; }
.csh__none + .link-arrow { margin-top: var(--s-5); }
/* the note row under a set of one: the ledger's own shape with a rule where
   the plate would be, so it reads as the next row and not as an error state.
   No plate, no hover, no stretched link - it is a note, not a study. */
.csh__note { border-top: 1px solid var(--line); }
.csh__note-h {
  margin-top: var(--s-4);
  font: var(--t-d3);
  letter-spacing: -.016em;
  color: var(--ink);
  max-width: 20ch;
  text-wrap: balance;
}
.csh__note .csrow__sum { margin-top: var(--s-4); color: var(--ink-2); }
.csh__note .link-arrow { margin-top: var(--s-6); }

.csrow { position: relative; }
.csrow--paper { background: var(--paper); }
.csrow--wash  { background: var(--wash); }
.csrow .hubplate { margin-top: 0; }
.csrow .hubplate__media img { transition: scale var(--dur-5) var(--ease-soft); }
/* an unresolved sector: the panel renders and the media is navy plus the
   masked inverse grid, exactly as .indrel__panel--flat. Never a broken image. */
.hubplate__media--flat { background: var(--navy); }

.csrow__ledger { padding-block: var(--s-9); }
.csrow__h {
  font: var(--t-d2);
  letter-spacing: -.018em;
  color: var(--ink);
  max-width: 46ch;
  text-wrap: balance;
  overflow-wrap: anywhere;
}
.csrow__h a { color: inherit; text-decoration: none; }
.csrow__h a::after { content: ""; position: absolute; inset: 0; }   /* whole-row target, plate included */
.csrow__sum { margin-top: var(--s-5); font: var(--t-lead); color: var(--ink-3); max-width: 52ch; }
/* the metrics as an inline ruled row: .csh-lead__stats renamed, rules kept */
.csrow__stats {
  display: flex; flex-wrap: wrap; gap: var(--s-6) var(--s-9);
  margin: var(--s-7) 0 0; padding: var(--s-5) 0 0;
  list-style: none;
  border-top: 2px solid var(--navy-100);
}
.csrow__num {
  display: block; font: var(--t-num-sm); font-variant-numeric: tabular-nums;
  letter-spacing: -.028em; color: var(--navy);
}
.csrow__lab { display: block; margin-top: var(--s-1); font: var(--t-cap); color: var(--ink-3); max-width: 22ch; }

.csrow:hover .csrow__h a,
.csrow:focus-within .csrow__h a {
  color: var(--navy-500); text-decoration: underline;
  text-underline-offset: 5px; text-decoration-thickness: 2px;
}
.csrow:hover .hubplate__media img { scale: 1.03; }
@supports selector(:has(*)) {
  .csrow:has(.csrow__h a:focus-visible) { outline: 2px solid var(--navy); outline-offset: -8px; }
  .csrow .csrow__h a:focus-visible { outline: none; }
}
@media (min-width: 768px) { .csrow__ledger { padding-block: var(--s-10); } }

/* --- reduced motion, high contrast (§12, §13) ---------------------------
   .ihero*, .pl-cta*, .hubplate* and .indrel* are already neutralised in
   the shared blocks above and below; this is the case-study addition. */
@media (prefers-reduced-motion: reduce) {
  .csrow .hubplate__media img { scale: none !important; transition: none !important; }
}
@media (prefers-contrast: more) {
  .csch__num { color: rgba(2, 45, 90, .34); }
  .csrow .hubplate__num { color: rgba(255, 255, 255, .46); }
}

/* =========================================================================
   EDITORIAL PHOTOGRAPHY  (client revision)
   The client read the interior pages as bland and asked for real business
   photography on every routed page, four to five images each. This is the
   whole grammar for it: one frame, one offset outline, two band cuts, one
   two-up and one in-column rail. Nine templates reuse these classes instead
   of each inventing a media component, which is what keeps the pages reading
   as one publication rather than nine stock-photo strips.

   The offset outline is next.css's .hero__media::before device (SPEC-01
   §5.3), at --r-lg. It lives on a WRAPPER because .phf carries
   overflow:hidden and would clip its own pseudo-element.

   No gold on any of these frames and no .frame__gold: SPEC-00 §3.5 caps gold
   at 14 instances per page, a service page already spends most of that on
   eyebrow ticks, and a gold bar repeated on four photographs stops reading
   as an accent at all.
   ========================================================================= */

.ph-set { position: relative; }
/* ≤560 the frame is the full column and a 14px offset would sit under the
   gutter, so the outline starts at 561 with the rest of the editorial
   furniture on this site. */
@media (min-width: 561px) {
  .ph-set::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: var(--z-pattern);
    translate: 14px 14px;
    border: 1.5px solid rgba(2, 45, 90, .16);
    border-radius: var(--r-lg);
    pointer-events: none;
  }
  .ph-set--flip::before { translate: -14px 14px; }
}

.phf {
  position: relative;
  z-index: var(--z-media);
  display: block;
  overflow: hidden;
  aspect-ratio: 3 / 2;                /* the masters are 3:2; this is the uncropped cut */
  border-radius: var(--r-lg);
  background: var(--navy-050);        /* the ground while the photograph decodes */
  outline: 1px solid rgba(2, 45, 90, .08);
  outline-offset: -1px;
  box-shadow: var(--sh-3);
}
/* the <picture> is a block of auto height, so a percentage height on the
   <img> would resolve to auto and object-fit would never govern the crop.
   Pinning the wrapper to the frame is what makes --po real. */
.phf picture { position: absolute; inset: 0; display: block; }
.phf img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--po, 50% 44%);   /* SPEC-00 §8.5: never the default crop */
}

/* NOTE (SPEC-P-R2 §2.5, applied). The seven static templates no longer call
   fa_ph(): they call fa_ph_plane() and own the box themselves, as planes,
   bled plates and column plates with no outline, no radius beyond --r-md and
   no shadow. .phf--wide, .ph-band, .ph-band--narrow, .ph-sect, .ph-pair,
   .ph-rail, .ab-off__media and .ct__loc-media had exactly those seven callers
   between them and are DELETED. .ph-set and .phf stay: templates/home-next.php
   still renders the insights art column through fa_ph().

   The services hub's own frames are .hubplate, which is not a .ph-set at all
   (SPEC-S2-R2 §4). The insights archive's are covers, below. */

/* =========================================================================
   SHARED PAGE OBJECTS  (SPEC-P-R2 §2.5, §3.9)
   Two objects the static set shares and no other template uses.
   ========================================================================= */

/* --- .pl-strip: a full-bleed row of bled plates -------------------------
   The replacement for .ph-pair. No frame, no radius, no shadow, no offset
   outline: the photographs ARE the surface and the only separation between
   them is the 2px navy gap the section ground shows through. Decorative in
   every use, so every <img> inside it carries alt="" and the strip carries no
   caption and no heading. A DIRECT child of the <section>, never a
   negative-margin bleed out of .container. */
.pl-strip { display: grid; gap: 2px; background: var(--navy-900); }
.pl-strip__plate { position: relative; height: 240px; overflow: clip; }
.pl-strip__plate picture { position: absolute; inset: 0; display: block; }
.pl-strip__plate img { width: 100%; height: 100%; object-fit: cover; object-position: var(--po, 50% 44%); }
@media (min-width: 768px) {
  .pl-strip { grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr); }
  .pl-strip__plate { height: 300px; }
}
@media (min-width: 1024px) { .pl-strip__plate { height: 360px; } }

/* --- .pl-cta: the hero's field, mirrored (SPEC-S1-R2 §8.5 grammar) ------
   Same field, same seam, same dissolve, reflected: About opens and closes on
   one gesture and the closing photograph does structural work instead of
   sitting between two things. The copy occupies the half the plane does not,
   so the empty right 45 per cent of the old navy slab is gone without a word
   being added to fill it. */
.pl-cta {
  position: relative;
  isolation: isolate;
  overflow: clip;
  display: flex;
  flex-direction: column;
  background: radial-gradient(150% 118% at 92% -6%, #063A70 0%, #022D5A 46%, #011E3D 100%);
  color: #fff;
}
.pl-cta__plane {
  position: relative;
  z-index: var(--z-overlay);
  order: 1;
  height: 220px;
  overflow: hidden;
}
@media (min-width: 561px) { .pl-cta__plane { height: 260px; } }
.pl-cta__plane picture { position: absolute; inset: 0; display: block; }
.pl-cta__plane img { width: 100%; height: 100%; object-fit: cover; object-position: var(--po, 50% 44%); }
.pl-cta__plane::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(1, 30, 61, .30) 0, transparent 34%,
                                      transparent 64%, rgba(1, 30, 61, .56) 100%);
}
.pl-cta__seam {
  order: 2;
  height: 3px;
  background: linear-gradient(90deg, var(--gold) 0 64%, var(--gold-600) 100%);
}
@media (min-width: 561px) { .pl-cta__seam { height: 4px; } }
.pl-cta__copy { position: relative; z-index: var(--z-content); order: 3; padding-block: var(--section-y); }
.pl-cta__h { font: var(--t-d2); letter-spacing: -.022em; color: #fff; max-width: 16ch; }
.pl-cta__lead {
  margin-top: var(--s-5);
  font: 400 17px/1.6 var(--font-ui);
  color: rgba(255, 255, 255, .80);
  max-width: 44ch;
}
.pl-cta__actions { display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-4); margin-top: var(--s-8); }
@media (max-width: 560px) {
  .pl-cta__actions { flex-direction: column; gap: var(--s-3); }
  .pl-cta__actions .btn { width: 100%; }
}
@media (min-width: 1120px) {
  .pl-cta { --seam: calc(50vw - 200px); min-height: 420px; justify-content: center; }
  /* the plane runs 0 to --seam, i.e. right: 100% - --seam. `inset: 0 var(--seam) 0 0`
     would paint everything EXCEPT the left --seam pixels, the mirror of what
     is wanted. Same correction next.css records for .hero__pattern. */
  .pl-cta__plane { position: absolute; inset: 0 calc(100% - var(--seam)) 0 0; order: 0; height: auto; }
  .pl-cta__plane::after {
    background:
      linear-gradient(270deg,
        rgba(2, 45, 90, .97)   0,
        rgba(2, 45, 90, .60) 120px,
        rgba(2, 45, 90, .26) 230px,
        rgba(2, 45, 90, .09) 100%);
  }
  .pl-cta__seam {
    position: absolute;
    left: var(--seam); top: 0; bottom: 0;
    z-index: var(--z-glass);
    width: 4px; height: auto;
    background: linear-gradient(180deg, var(--gold) 0 64%, var(--gold-600) 100%);
  }
  .pl-cta__copy { padding-block: var(--s-12); padding-left: min(520px, calc(50vw - 160px)); }
  .pl-cta__copy > * { max-width: 600px; }
}

/* =========================================================================
   REDUCED MOTION  (SPEC-00 §6.7)
   next.css's block covers everything it knew about. These are the hover
   transforms this file introduces, plus the drawn elements SPEC-S1 adds:
   reduced motion collapses the transition, not the state, so a node that
   rests at scale(.5)/opacity 0 and a connector that rests at scaleY(0) must
   be forced to their FINAL values or they are simply missing. That is the
   most likely single defect on the service template.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  /* The interior hero's own contract is stated in the SPEC-IH-R3 block at the
     top of this file. These are the navy CTA bands and the body components. */
  .pl-cta *, .pl-cta *::before, .pl-cta *::after { animation: none !important; }
  /* the ledger link rows never move: their whole feedback is colour, which
     §6.7 keeps. Only the arrow travel is cancelled. */
  .pl-row:hover .pl-row__go svg { transform: none !important; }

  .svc-more__chev, .svc-cov__glyph, .svc-cov__go { transition: none !important; }
  /* the gold row edge is gated by OPACITY as well as transform, so forcing
     scaleY(1) here cannot reveal every edge at once */
  .svc-cov__link::after { transform: scaleY(1) !important; }
  .svc-q__chev      { transition: none !important; }

  /* SPEC-S1-R2 §10.1 and SPEC-S2-R2 §8.4. Reduced motion collapses the
     transition, not the state: an element that RESTS at scaleY(0) or
     clip-path: inset(0 100% 0 0) must be forced to its final value or it is
     simply missing. Every gold edge is gated by opacity and not by transform,
     precisely so that neutralising transform here cannot light up every edge
     on the page at once. */
  /* .svc-cta is NOT in this list any more: the closer and its reduced-motion
     entry both live in next.css now. */
  .hubplate *, .hubplate *::before, .hubplate *::after,
  .hubdir *, .hubloc *, .hubplat * { animation: none !important; }
  .hubplate__seam { transform: none !important; }
  .hubplate__media { clip-path: none !important; }
  .hubplate__media img { transform: none !important; }
  .hubloc__go { transition: color var(--dur-1) var(--ease-out) !important; }
  .hubloc__link:hover .hubloc__go { transform: none !important; }

  /* colour feedback is kept under §6.7, so every hover and focus fill stays */
  .hubdir__list a:hover, .hubloc__link:hover::before, .hubtoc__link:hover::before { opacity: 1; }
}

/* =========================================================================
   HIGH CONTRAST AND NO-BACKDROP-FILTER  (SPEC-S1-R2 §12, SPEC-S2-R2 §11.13)
   ========================================================================= */
@media (prefers-contrast: more) {
  /* the closer's high-contrast entry moved to next.css with the closer. */
  .hubplate__media::after {
    background: linear-gradient(90deg, #022D5A 0, rgba(2, 45, 90, .88) 120px, rgba(2, 45, 90, .44) 100%);
  }
  .hubplate--flip .hubplate__media::after {
    background: linear-gradient(270deg, #022D5A 0, rgba(2, 45, 90, .88) 120px, rgba(2, 45, 90, .44) 100%);
  }
  .svc-del__num { color: rgba(2, 45, 90, .34); }
  /* SPEC-SI-R3 §9.11 */
  .svc-cov__list, .svc-cov__cell { border-color: var(--navy-300); }
  .ihero__chip { outline: 1px solid var(--navy-100); }

  /* SPEC-P-R2 §11 item 10 */
  .pl-cta__plane::after {
    background: linear-gradient(270deg, #022D5A 0, rgba(2, 45, 90, .86) 120px, rgba(2, 45, 90, .42) 100%);
  }
  .bk__item-num { color: rgba(255, 255, 255, .38); }
}

/* ===========================================================================
   ROUND THREE, SECOND PASS. Three bands the client called out on the service
   single: the approach band ("looks average"), the related band ("too much
   empty space") and the closing CTA ("doesn't look good"). Same vocabulary in
   all three - hairline rules, --navy-050 fills, no glass, no shadow, no glow,
   gold only on the eyebrow tick and the primary button.
   ======================================================================== */

/* --- the approach register (was .duo__* inside .svc-why) -------------------
   Superseded on the service template by .s4-why__* (SPEC-SVC-R4 §4.6) and on
   the industry template by .i4-work__* (SPEC-IND-R4 §4.4). Kept here because
   page-about.php still renders .why-reg__*. */
.why-reg {
  margin-top: var(--s-7);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.why-reg__row {
  display: grid;
  padding-block: 24px 26px;
  border-bottom: 1px solid var(--line);
}
.why-reg__row:last-child { border-bottom: 0; }
/* 12px, tabular, --ink-3 at 5.6:1. Decoration, aria-hidden, and deliberately
   NOT the 44px pale numeral the client rejected on the other two bands. */
.why-reg__num {
  font: 600 12px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: .08em;
  color: var(--ink-3);
}
.why-reg__term {
  margin: 10px 0 0;
  font: 600 21px/1.26 var(--font-display);
  letter-spacing: -.014em;
  color: var(--navy);
  text-wrap: balance;
}
/* FLUSH LEFT, margin-inline-start explicitly zeroed: <dd> carries a 40px UA
   indent and that indent was the "oddly indented body copy" on the old band. */
.why-reg__desc {
  margin: 8px 0 0;
  font: var(--t-body-sm);
  color: var(--ink-2);
  max-width: 62ch;
}
.why-reg__desc :is(p, li) + :is(p, li) { margin-top: var(--s-2); }
.why-reg__desc :is(ul, ol) { padding-left: var(--s-5); list-style: disc outside; }
@media (min-width: 1024px) {
  .why-reg__row { padding-block: 26px 30px; }
  .why-reg__term { font-size: 22px; }
}
/* Two-up ONLY on an even count (PHP writes data-cols), so the grid can never
   end on a lone cell beside a hole. The column rule is the same hairline. */
@media (min-width: 1180px) {
  .why-reg[data-cols="2"] {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--s-10);
  }
  .why-reg[data-cols="2"] > .why-reg__row:nth-last-child(-n+2) { border-bottom: 0; }
}

/* --- the related card grid: DELETED (SPEC-SVC-R4 §4.11) -----------------
   .rel-grid / .rel-card are renamed .s4-rel__grid / .s4-rel__card and gain a
   mark per card. service-next.php was their only consumer. */

/* --- the contained closing plate: DELETED ---------------------------------
   The modifier existed only to turn the full-bleed .svc-cta into a contained
   plate on the templates that had migrated, while post-next.php,
   industries-hub.php and insights-archive.php still rendered the full-bleed
   original. All fourteen now include templates/partials/cta-close.php, there
   is no second form of the band left, and §8 above IS the contained plate.
   A template that still carries .svc-cta--contained in its class list is
   unaffected: nothing matches it. */

/* --- the register's optional head row (numeral + drawn mark) ---------------
   .why-reg on the service approach band has a numeral and nothing else; the
   About page's four principles carry a glyph beside it. Both render through
   the same rows, so the mark is an OPTIONAL child rather than a second
   component: a row without .why-reg__head is byte-identical to before. */
.why-reg__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
}
.why-reg__ico { display: block; width: 22px; height: 22px; color: var(--navy-300); }
.why-reg__ico svg { display: block; width: 100%; height: 100%; }
/* the About register sits in the same sticky-aside grid as the service one */
.ab-work { padding-block: var(--section-y); background: var(--paper); }

/* =========================================================================
   INTERIOR MOTION PASS  (SPEC-VH-R3 §B5, recipes T1-T7)
   Appended LAST on purpose. Every rule below either introduces a property no
   earlier block sets, or deliberately re-states one that an earlier block set
   at equal specificity; putting the block at the foot of the file is what
   makes the second kind win on source order. No shorthand-everything
   transition, nothing animates a property other than transform/opacity/translate,
   and nothing carries a resting `will-change`.
   ========================================================================= */

/* --- T1: the interior hero plate holds --par-1 -------------------------
   The page's ONE parallax element. `translate`, not `transform`, so it
   composes with the .hl load keyframes instead of clobbering them, and the
   range starts at 0 so the plate is exactly where the layout put it at rest
   and only drifts once the reader scrolls. 24px of travel against a plate
   that is 432px tall can never expose an edge, because the plate is not
   cropped to its frame: it IS the frame. */
@supports (animation-timeline: scroll(root block)) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 1024px) {
    .ihero__plate {
      animation: ihero-par linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 420px;
    }
    @keyframes ihero-par {
      from { translate: 0 0; }
      to   { translate: 0 calc(var(--par-1) * -1); }
    }
  }
}

/* --- T2: sticky short columns, measured -------------------------------
   Travel = (tall column) - (short column) - (header-h + 24). Measured at
   1440 on staging with real content, 2026-09-02.

   About / story: copy 606px, plate 325px, header 105 -> 176px. Clears the
   ~120px bar. `align-self: start` is already set at >=1024 by the block
   above; position is all that is added. The plate's own .r--lg transform
   lives on the sticky element itself, which is allowed: the prohibition is
   on a transform or an overflow clip on an ANCESTOR. */
@media (min-width: 1024px) {
  .ab-story__plate {
    position: sticky;
    top: calc(var(--header-h, 80px) + 24px);
  }
}

/* .svc-why__aside: the rule that switched it back to static went with the
   band. .s4-why (SPEC-SVC-R4 §4.6) never declares position at all, so there is
   nothing left to undo. */

/* .svc-faq__aside: KEPT, but gated on the question count. Measured travel at
   1440 is +118px on /industries/non-profit/ (5 questions, all closed) and
   negative on the two service pages sampled, and it only ever grows: every
   accordion a reader opens adds 100-200px to the column beside the aside and
   nothing to the aside. Four or more questions is the cheapest honest floor.
   Where :has() is unavailable the aside is static, which is a correct layout.
   This re-states `position` later in the file and at higher specificity than
   the @media block above, so it wins on both counts. */
@media (min-width: 1024px) {
  :is(.page-service, .page-industry, .page-about, .page-thank-you) .svc-faq__grid .svc-faq__aside {
    position: static;
  }
}
@supports selector(:has(*)) {
  @media (min-width: 1024px) {
    .svc-faq__grid:has(.svc-faq__list > :nth-child(4)) .svc-faq__aside {
      position: sticky;
      top: calc(var(--header-h, 80px) + 24px);
      align-self: start;
    }
  }
}

/* --- T5: the closer settles as ONE plate: MOVED to next.css ------------
   The recipe is unchanged, its home is not. */

/* --- no-JS (§B7) ------------------------------------------------------
   Every .r above is scoped to .js, so the no-JS document already renders
   finished. These are the two states that are NOT expressed through .r. */
html:not(.js) .ihero__plate { translate: none; }

/* --- reduced motion (§B7) ---------------------------------------------
   next.css already forces every .r and .r--rule to its finished state and
   gates band-arrive / .par-* behind no-preference. The hero plate's
   scroll-driven parallax is gated at its @media above; this is the belt to
   that braces, because a scroll-driven animation is not a transition and the
   global `animation-duration: .01ms` override does not stop a timeline. */
@media (prefers-reduced-motion: reduce) {
  .ihero__plate { animation: none !important; translate: none !important; }
}

/* =========================================================================
   INDUSTRY BAND 2 | THE OPENING RECORD (SPEC-IO-R3)
   Replaces the deleted .indopen* block. Head row (7/5) + one wide plate +
   capped prose. No glass, no shadow, no sticky (§6: measured travel is
   48-140px, below the 120px threshold on nine of the eleven). Gold appears
   only on the eyebrow tick, which is the inherited .eyebrow rule. Hairlines
   from --line.
   ========================================================================= */
.io { padding-block: var(--section-y); }
.io--paper { background: var(--paper); }
.io--wash  { background: var(--wash); }

/* --- head row ----------------------------------------------------------- */
.io__head { display: grid; gap: var(--s-8); }
@media (min-width: 1024px) {
  .io__head {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    column-gap: var(--s-11);
    align-items: start;
  }
}
@media (min-width: 1180px) { .io__head { column-gap: var(--s-12); } }

.io__eyebrow { color: var(--navy); }
.io__h2 {
  margin: var(--s-4) 0 0;
  font: var(--t-d2);
  color: var(--ink);
  max-width: 20ch;
  text-wrap: balance;
}
.io__stand {
  margin: var(--s-5) 0 0;
  font: var(--t-lead);
  color: var(--ink-2);
  max-width: 58ch;
  text-wrap: pretty;
}

/* chips: the audience, from ind_kicker. Outline, not fill; not links. */
.io__chips { margin: var(--s-6) 0 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: var(--s-2); }
.io__chip {
  font: var(--t-label);
  color: var(--ink-2);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 7px var(--s-4);
  white-space: nowrap;
}
.io--paper .io__chip { background: var(--wash); }

/* facts panel: counts of what the page renders, each one a jump. */
.io__facts {
  background: var(--navy-050);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-6);
}
.io__facts-t { margin: 0 0 var(--s-3); font: var(--t-eyebrow); letter-spacing: .12em; text-transform: uppercase; color: var(--navy); }
.io__list { margin: 0; padding: 0; list-style: none; }
.io__list > li + li { border-top: 1px solid var(--navy-100); }
.io__fact {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr) 16px;
  align-items: center;
  column-gap: var(--s-4);
  padding-block: 13px;
  text-decoration: none;
  color: inherit;
  border-radius: var(--r-xs);
  transition: background 160ms var(--ease-soft), color 160ms var(--ease-soft);
}
.io__fact:hover { background: var(--paper); }
.io__fact:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; }
.io__n { font: 600 26px/1 var(--font-display); font-variant-numeric: tabular-nums; letter-spacing: -.03em; color: var(--navy); }
.io__lbl { font: var(--t-body-sm); color: var(--ink-2); }
.io__fact svg { width: 16px; height: 16px; color: var(--navy-300); display: block; transition: transform 160ms var(--ease-soft); }
.io__fact:hover svg { transform: translateX(3px); }

/* --- plate row: ONE photograph, contained, full container width --------- */
.io__plate {
  position: relative;
  isolation: isolate;
  margin: var(--s-9) 0 0;
  border-radius: var(--r-lg);
  overflow: clip;
  aspect-ratio: 21 / 9;
  background: var(--navy-050);
  border: 1px solid var(--line);
}
@media (max-width: 767px) { .io__plate { aspect-ratio: 4 / 3; border-radius: var(--r-md); margin-top: var(--s-7); } }
.io__plate :is(picture, img) { display: block; width: 100%; height: 100%; object-fit: cover; }

/* --- the plate's scrim and overlay -------------------------------------
   The duotone foot of .indrel__panel, deepened, because this one carries
   TEXT and that one does not. The brief's ramp (transparent at 45% to .82 at
   the foot) puts the eyebrow's own top edge at roughly .50 alpha, which
   measures 2.6:1 against white on a white-biased frame. The ramp therefore
   opens at 38% and reaches .82 by 88%: at the overlay's top edge the scrim is
   >= .61, which measures 4.65:1 white-on-scrim over the worst frame in the
   library, and 6.0:1 at the foot. Nothing is full-bleed: the scrim is inset
   to the figure and clipped by its radius. */
.io__scrim {
  content: "";
  position: absolute; inset: 0;
  z-index: var(--z-overlay);
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(2, 45, 90, 0) 38%,
    rgba(2, 45, 90, .58) 68%,
    rgba(2, 45, 90, .82) 88%,
    rgba(2, 45, 90, .88) 100%);
}
.io__over {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: var(--z-content);
  padding: var(--s-6);
  max-width: 46ch;
}
@media (min-width: 1180px) { .io__over { padding: var(--s-7) var(--s-8); } }
.io__over-eyebrow {
  margin: 0;
  font: var(--t-eyebrow);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .88);
}
.io__over-line {
  margin: var(--s-2) 0 0;
  font: 600 20px/1.3 var(--font-display);
  letter-spacing: -.014em;
  color: #fff;
}
@media (min-width: 1180px) { .io__over-line { font-size: 24px; } }
.io__over-acts {
  margin-top: var(--s-4);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-5);
}
/* the secondary action is a white text link, never a second gold button: the
   .btn--primary above it is the band's only gold besides the eyebrow tick. */
.io__over-alt {
  font: var(--t-body-sm);
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: rgba(255, 255, 255, .5);
}
.io__over-alt:hover { text-decoration-color: #fff; }
.io__over-alt:focus-visible { outline: 2px solid #fff; outline-offset: 3px; border-radius: 2px; }
/* below 768 the overlay stacks: the line takes the full plate width and the
   two actions go one per row so neither is a hit target under 44px. */
@media (max-width: 767px) {
  .io__over { max-width: none; padding: var(--s-5); }
  .io__over-line { font-size: 18px; }
  .io__over-acts { flex-direction: column; align-items: stretch; gap: var(--s-3); }
  .io__over-acts .btn { justify-content: center; }
  .io__over-alt { text-align: center; }
}

/* --- prose row ---------------------------------------------------------- */
.io__prose-wrap { margin-top: var(--s-9); padding-top: var(--s-7); border-top: 1px solid var(--line); }
.io__prose > :is(p, ul, ol) { font: var(--t-body); color: var(--ink-2); max-width: 65ch; }
.io__prose > * + * { margin-top: var(--s-5); }
.io__prose > h3 { font: var(--t-d4); color: var(--navy); margin-top: var(--s-7); }
/* the opener, set up one notch. No drop cap: a floated capital in a two-column
   set collides with the column rule at 1180. */
.io__prose > p:first-of-type::first-line { font-weight: 600; color: var(--ink); }

@media (min-width: 1180px) {
  .io__prose { columns: 2; column-gap: var(--s-12); column-rule: 1px solid var(--line-2); }
  .io__prose > * { max-width: none; break-inside: avoid; }
  .io__prose > *:first-child { margin-top: 0; }
}
.io__more { margin-top: var(--s-7); }

/* --- source order below 1024 (§7) ---------------------------------------
   eyebrow, headline, standfirst, chips, photo, prose, facts. The facts panel
   is the one element whose DOM position is not its mobile position, so the
   band becomes a flex column and .io__head steps out of the way. The two
   children that lose their .container wrapper to `display: contents` take the
   container's own measure and gutter back explicitly. */
@media (max-width: 1023px) {
  .io { display: flex; flex-direction: column; }
  .io__head { display: contents; }
  .io__lede, .io__facts {
    width: 100%;
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: var(--gutter);
  }
  .io__lede { order: 1; }
  .io__plate-row { order: 2; }
  .io__prose-wrap { order: 3; }
  .io__facts { order: 4; margin-top: var(--s-8); }
}

/* --- reveal ------------------------------------------------------------
   The band uses the shared .r vocabulary from next.css and adds nothing to it:
   .r, .r--sm, .r--rule and .r--plate carry their own travel and duration, and
   the only per-element value here is the --rd stagger set inline. */
@media (prefers-reduced-motion: reduce) {
  .io *, .io .r { animation: none !important; transition: none !important; opacity: 1 !important; transform: none !important; }
}
@media (prefers-contrast: more) {
  .io__chip, .io__facts, .io__plate { border-color: var(--navy-300); }
  .io__list > li + li { border-top-color: var(--navy-300); }
}

/* ===========================================================================
   ROUND FOUR, THE SERVICE SINGLE  (SPEC-SVC-R4)
   templates/service-next.php only. Twelve bands, five of them landmarks.

   The rule this block is built on, verbatim from the spec: every band is
   designed as a picture and a paragraph at once. Before a band's CSS is
   written its asset is chosen and sized - an illustration, a diagram, a
   photograph, a portrait, a marquee, or a number set at display scale - and a
   band that cannot name its asset is not a band, it is a list, and it gets
   folded into the band above it.

   What is NEW here rather than renamed: the fit band (2), the reading rail in
   the overview (3), the scope bar and head figure on the register (4), the
   drawn spine on the process plane (5), the pull-quote in the approach aside
   (6), the whole navy proof band (7), the price ladder (9), the per-row icons
   on the accordion (10) and the per-card icons on related (11).

   Deletions that ship with it, all of them service-only consumers:
   .svc-open*, .svc-proc / .svc-runs* / .svc-run*, .svc-rel__* and
   .rel-grid / .rel-card. .svc-cov__*, .svc-del__*, .why-reg__*, .svc-faq /
   .svc-q* and .svc-more stay: the case-study, industry, about and thank-you
   templates still render them.

   SPEC-SVC-R4 §12.4 asked for a separate assets/single-r4.css. It ships here
   instead, on the orchestrator's ruling, so the two singles keep the two
   render-blocking requests SPEC-00 §8.7 budgets for and no template grows a
   fourth <link>.
   ======================================================================== */

/* --- the vertical rhythm: five landmarks, seven quieter bands ------------
   --section-y is untouched and nothing else on the site changes. Landmark
   bands (3, 4, 5, 7) get more air; connective bands (2, 6, 9, 10, 11) get
   less, which is what gives the page a spine you can see at 12% zoom. */
body.page-service,
body.page-industry {
  --band-y:    clamp(56px, 5.4vw, 96px);
  --band-y-lg: clamp(72px, 7.4vw, 128px);
}

/* --- shared R4 primitives ------------------------------------------------
   An illustration is generated on white and mounted on a tint with
   mix-blend-mode: multiply, so the white becomes the plate's own --navy-050
   and the navy and gold survive. SPEC-00 §5.3 bans `screen` and
   `plus-lighter`; `multiply` is not a glow and is not on that list. Where the
   browser has no blend mode the plate turns --paper and the drawing mounts on
   white, which is the state the file was generated in. */
.s4-ill { display: block; width: 100%; height: 100%; object-fit: cover; mix-blend-mode: multiply; }
@supports not (mix-blend-mode: multiply) {
  .s4-fit__plate, .s4-cov__art, .s4-faq__art, .s4-price__art { background: var(--paper); }
  .s4-ill { mix-blend-mode: normal; }
}

/* the index bar's mark. --navy-300 at rest is 3.4:1, which is the non-text UI
   floor, and it is never the only signal: the label beside it carries the
   meaning and the cell is a link either way. */
.s4-ix__ico { display: block; width: 20px; height: 20px; color: var(--navy-300); margin-bottom: 4px; }
.s4-ix__ico svg { display: block; width: 100%; height: 100%; }
.ihero__link:hover .s4-ix__ico,
.ihero__link:focus-visible .s4-ix__ico { color: var(--navy); }
@media (max-width: 767px) { .s4-ix__ico { display: none; } }
/* At five cells the 168px auto-fit floor wraps the bar to 3 + 2 and the shelf
   grows past SPEC-IH §4.4's ceiling. The floor drops to 140 at >=1180 only,
   which puts five cells on one row at 148.6 each. Below 1180 the bar wraps and
   carries min-height, which it already did. */
@media (min-width: 1180px) {
  .page-service .ihero__cells[data-cells="5"] { grid-template-columns: repeat(5, minmax(140px, 1fr)); }
  /* five labels in a 148px cell: 17px wraps "Why this way" onto two lines and
     the shelf grows a row. 16px holds every label on this template on one. */
  .page-service .ihero__cells[data-cells="5"] .ihero__lbl { font-size: 16px; }
  .page-service .ihero__cells[data-cells="5"] .ihero__link { padding-right: var(--s-3); }
}

/* ========================================================================
   BAND 2 | WHO IT IS FOR  (§4.2)
   The page's single most reader-facing string set at display scale beside a
   picture of the reader, then three fit signals derived from the page's own
   contents. Shape B, 5/7, narrow column left.
   ===================================================================== */
.s4-fit        { padding-block: var(--band-y); }
.s4-fit--paper { background: var(--paper); }
.s4-fit--wash  { background: var(--wash); }
.s4-fit__grid  { display: grid; gap: var(--s-8); }

.s4-fit__art   { margin: 0; }
.s4-fit__plate {
  position: relative;
  display: block;
  width: 100%; max-width: 300px;
  aspect-ratio: 1 / 1;
  border-radius: var(--r-lg);
  border: 1px solid var(--navy-100);
  background: var(--navy-050);
  overflow: clip;
}
/* a missing file leaves this sized, tinted, hairlined box and nothing else:
   no broken-image mark, no alt text, and the band's copy is unaffected */
.s4-fit__cap {
  margin-top: var(--s-5);
  padding-left: var(--s-4);
  border-left: 3px solid var(--navy);
  font: var(--t-body-sm);
  color: var(--ink-2);
  max-width: 34ch;
}

.s4-fit__h2 {
  margin-top: var(--s-4);
  font: var(--t-d2);
  letter-spacing: -.022em;
  color: var(--ink);
  text-wrap: balance;
}
.s4-fit__list   { margin-top: var(--s-8); list-style: none; border-top: 1px solid var(--line); }
.s4-fit__signal {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  gap: var(--s-4);
  align-items: start;
  padding-block: var(--s-4);
  border-bottom: 1px solid var(--line);
}
.s4-fit__ico     { display: block; width: 22px; height: 22px; margin-top: 2px; color: var(--ink-3); }
.s4-fit__ico svg { display: block; width: 100%; height: 100%; }
.s4-fit__txt     { font: var(--t-body); color: var(--ink-2); max-width: 52ch; }

@media (min-width: 561px) {
  .s4-fit__plate  { max-width: 360px; }
  .s4-fit__signal { padding-block: 18px; }
  .s4-fit__ico    { width: 24px; height: 24px; }
}
@media (min-width: 1024px) {
  .s4-fit       { padding-block: clamp(72px, 6vw, 96px); }
  .s4-fit__grid { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); column-gap: var(--s-11); align-items: center; }
  /* no illustration on disk means no track for it: the copy takes the measure
     rather than sitting beside an empty tinted rectangle (ASSETS-R4 §4.5) */
  .s4-fit__grid--noart { grid-template-columns: minmax(0, 1fr); }
  .s4-fit__plate { max-width: 100%; }
  .s4-fit__h2   { max-width: 22ch; }
}
@media (min-width: 1180px) {
  .s4-fit__grid   { column-gap: var(--s-12); }
  /* capped at 420 so the drawing is never rendered above its 440w source at
     1x, which is what keeps the 440w file in play on every desktop DPR <= 1.9 */
  .s4-fit__plate  { max-width: 420px; }
  .s4-fit__signal { padding-block: var(--s-5); }
}

/* ========================================================================
   BAND 3 | OVERVIEW, AND THE PAGE'S ONE READING RAIL  (§4.3)
   Every word R3 restored, with the right track turned into a short column
   that travels with the reader: the post's own featured image, a key number
   counted off the register below, and a pull-quote lifted whole from the
   page's own prose. Shape C, 7/5, narrow column right.
   ===================================================================== */
.s4-open        { padding-block: var(--band-y-lg); }
.s4-open--wash  { background: var(--wash); }
.s4-open--paper { background: var(--paper); }
.s4-open__grid  { display: grid; gap: var(--s-8); align-items: start; }

.s4-open__stand {
  font: var(--t-d3); letter-spacing: -.014em; color: var(--ink);
  max-width: 46ch; text-wrap: balance;
}
.s4-open__prose         { margin-top: var(--s-7); padding-top: var(--s-7); border-top: 1px solid var(--line-2); }
.s4-open__prose > * + * { margin-top: var(--s-5); }
.s4-open__prose p       { font: var(--t-body); color: var(--ink-2); max-width: 62ch; }
.s4-open__prose h3      {
  margin-top: var(--s-8);
  font: 600 21px/1.3 var(--font-display);
  letter-spacing: -.016em;
  color: var(--navy);
}
.s4-open__prose :is(ul, ol) { padding-left: var(--s-5); list-style: disc outside; }
.s4-open__prose ol          { list-style: decimal outside; }
.s4-open__prose li          { font: var(--t-body); color: var(--ink-2); max-width: 60ch; }
.s4-open__prose li + li     { margin-top: var(--s-2); }
.s4-open__prose li::marker  { color: var(--navy-300); }
.s4-open__prose blockquote  {
  padding-left: var(--s-5);
  border-left: 3px solid var(--gold);
  font: var(--t-d3);
  color: var(--navy);
}

.s4-open__rail-in > * + * { margin-top: var(--s-6); }
.s4-open__plate {
  margin: 0;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--navy-050);
  overflow: clip;
  aspect-ratio: 3 / 2;
}
.s4-open__plate :is(img, picture) {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: var(--po, 50% 42%);
}

/* a description of the document, at --t-num. Never a percentage, a dollar
   figure, a year count or a client count. */
.s4-open__num {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s-4); align-items: baseline;
  padding: var(--s-5) 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.s4-open__n  { font: var(--t-num); font-variant-numeric: tabular-nums; letter-spacing: -.032em; color: var(--navy); }
.s4-open__nl { font: var(--t-body-sm); color: var(--ink-3); }

.s4-open__pull p {
  padding-left: var(--s-5);
  border-left: 3px solid var(--gold);
  font: 600 clamp(17px, .5vw + 15px, 20px)/1.45 var(--font-ui);
  letter-spacing: -.006em;
  color: var(--navy);
  max-width: 32ch;
}

/* Below 1180 the rail is a normal block AFTER the standfirst and BEFORE the
   prose, so a reader on a laptop gets picture-then-argument. No `order` on
   content: .s4-open__main goes display: contents so its two children become
   grid items in their own right. That is safe here because the wrapper
   carries no role and the band's heading is a sibling. */
@media (max-width: 1179px) {
  .s4-open__grid  { grid-template-columns: minmax(0, 1fr); }
  .s4-open__main  { display: contents; }
  .s4-open__stand { order: 1; }
  .s4-open__rail  { order: 2; }
  .s4-open__prose { order: 3; }
}
@media (min-width: 1180px) {
  .s4-open__grid  { grid-template-columns: minmax(0, 1fr) 400px; column-gap: var(--s-11); }
  .s4-open__plate { aspect-ratio: 4 / 3; border-radius: var(--r-lg); }
  /* Sticky needs >=240px of travel (SPEC-00 §6.4). The rail is about 562px and
     the prose column runs 900-2,100px on the measured corpus, so the gate is
     PHP's $fa_open_stick and not this rule: 71 of 100 posts stick and the
     other 29 hold the rail at the top of its track. A 58px sticky reads as a
     bug and does not ship. */
  .s4-open__rail.is-sticky .s4-open__rail-in {
    position: sticky;
    top: calc(var(--header-h, 80px) + var(--s-6));
    z-index: var(--z-rail);
  }
}
@media (min-width: 1440px) {
  .s4-open__grid { grid-template-columns: minmax(0, 1fr) 440px; column-gap: var(--s-12); }
}

/* ========================================================================
   BAND 4 | WHAT YOU GET, THE REGISTER GRID  (§4.4)
   Carried over from the amendment the client approved in every structural
   respect, plus the two things it was missing: a scope bar that draws the
   whole engagement in one 28px picture, and an illustration in the band head.
   The full-bleed --paper panel inside a --wash band is gone; the 72x3 gold
   mark moved to the register's own top rule.
   ===================================================================== */
.s4-cov        { padding-block: var(--band-y-lg); overflow: clip; }
.s4-cov--paper { background: var(--paper); }
.s4-cov--wash  { background: var(--wash); }

.s4-cov__head  { max-width: none; display: grid; gap: var(--s-6); }
.s4-cov__count {
  margin-top: var(--s-3);
  font: 600 13px/1.4 var(--font-ui);
  letter-spacing: .02em;
  color: var(--ink-3);
}
/* decorative and below 1024 there is no room for both it and the register */
.s4-cov__art { display: none; margin: 0; }
@media (min-width: 1024px) {
  .s4-cov__head { grid-template-columns: minmax(0, 1fr) 240px; column-gap: var(--s-9); align-items: start; }
  .s4-cov__art  {
    display: block; width: 240px; aspect-ratio: 1 / 1;
    border-radius: var(--r-md); border: 1px solid var(--navy-100);
    background: var(--navy-050); overflow: clip;
  }
}
@media (min-width: 1180px) {
  .s4-cov__head { grid-template-columns: minmax(0, 1fr) 280px; }
  .s4-cov__art  { width: 280px; }
}

/* THE SCOPE BAR IS GONE (owner ruling on the first R4 render). It drew n
   equal pale segments above the register and said one thing at rest: this
   engagement has n named parts. The "6 items" line 40px above it already says
   that in words, and the bar read as a skeleton loader waiting for content.
   Numerals inside it would duplicate the numerals on the cells below it, and a
   caption that appears only on hover is invisible at rest and on every touch
   device, so it is removed rather than decorated, on both singles. The
   preserveAspectRatio="none" allowance now covers two drawings, not three:
   .s4-price__bar and .i4-model__svg, plus the .s4-spine rails. */

/* The 72x3 gold mark opens the register. It is the band's ONE gold instance
   besides the eyebrow tick, and it sits on the list's top rule rather than on
   the corner of a box. */
.s4-cov__list {
  --cell-x: 0px;
  --cell-bleed: 16px;
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  margin-top: var(--s-7);
  margin-inline: calc(var(--cell-x) * -1);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  list-style: none;
}
.s4-cov__list::before {
  content: "";
  position: absolute; left: var(--cell-x); top: -2px;
  width: 72px; height: 3px;
  background: var(--gold);
}
.s4-cov__cell,
.s4-cov__link {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-content: start;
}
.s4-cov__cell {
  position: relative;
  padding: 22px var(--cell-x) 26px;
}
.s4-cov__cell + .s4-cov__cell { border-top: 1px solid var(--line); }
.s4-cov__cell > .s4-cov__link {
  position: relative;
  isolation: isolate;
  grid-column: 1 / -1;
  text-decoration: none;
}
/* a cell with no `url` is not a link and has no state of any kind: a hover on
   a non-interactive row is a false affordance */
.s4-cov__link::before {
  content: "";
  position: absolute;
  inset: -22px calc(var(--cell-bleed) * -1) -26px;
  z-index: -1;
  background: var(--navy-050);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}
.s4-cov__link::after {
  content: "";
  position: absolute;
  left: calc(var(--cell-bleed) * -1); top: -22px; bottom: -26px;
  z-index: -1;
  width: 3px;
  background: var(--gold);
  opacity: 0;
  transform: scaleY(.4); transform-origin: center;
  transition: opacity var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out);
}
.s4-cov__link:hover::before,
.s4-cov__link:focus-visible::before { opacity: 1; }
.s4-cov__link:hover::after,
.s4-cov__link:focus-visible::after { opacity: 1; transform: scaleY(1); }
.s4-cov__link:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; border-radius: var(--r-sm); }

/* the mark is a meaningful graphic, so it takes --ink-3 at 5.6:1 and never
   --navy-300 at 3.04:1. It NEVER animates: it is a label. */
.s4-cov__glyph {
  grid-column: 1; grid-row: 1;
  display: block;
  width: 28px; height: 28px;
  color: var(--ink-3);
  transition: color var(--dur-2) var(--ease-out);
}
.s4-cov__glyph svg { display: block; width: 100%; height: 100%; }
.s4-cov__ix {
  grid-column: 2; grid-row: 1;
  align-self: center;
  font: 600 12px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: .08em;
  color: var(--ink-3);
}
.s4-cov__title {
  grid-column: 1 / -1;
  margin-top: var(--s-4);
  font: 600 20px/1.26 var(--font-display);
  letter-spacing: -.014em;
  color: var(--navy);
  overflow-wrap: anywhere;
  text-wrap: balance;
}
.s4-cov__text { grid-column: 1 / -1; margin-top: 10px; font: var(--t-body-sm); color: var(--ink-2); max-width: 46ch; }
.s4-cov__text :is(p, li) + :is(p, li) { margin-top: var(--s-2); }
.s4-cov__text :is(ul, ol) { padding-left: var(--s-5); list-style: disc outside; }
.s4-cov__go {
  grid-column: 1 / -1;
  margin-top: var(--s-4);
  width: 16px; height: 16px;
  color: var(--ink-3);
  transition: color var(--dur-2) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.s4-cov__go svg { display: block; width: 16px; height: 16px; }
.s4-cov__link:hover :is(.s4-cov__glyph, .s4-cov__ix, .s4-cov__go),
.s4-cov__link:focus-visible :is(.s4-cov__glyph, .s4-cov__ix, .s4-cov__go) { color: var(--navy); }
.s4-cov__link:hover .s4-cov__go,
.s4-cov__link:focus-visible .s4-cov__go { transform: translateX(4px); }
.s4-cov__link:hover .s4-cov__title,
.s4-cov__link:focus-visible .s4-cov__title {
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 5px;
  text-decoration-color: rgba(2, 45, 90, .35);
}

/* Void cells close the last row's rule and stop the dividers where they
   should. Empty, aria-hidden, never in the accessibility tree, and hidden
   outright below 1024 where the grid reflows and they could land mid-list. A
   hole is NEVER filled with a photograph, a call to action, or an "and more"
   string. */
.s4-cov__cell--void { display: none; }

@media (min-width: 768px) {
  .s4-cov__list { --cell-x: 20px; --cell-bleed: 20px; }
  .s4-cov__list[data-cols="2"],
  .s4-cov__list[data-cols="3"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .s4-cov__list[data-cols="2"] > .s4-cov__cell,
  .s4-cov__list[data-cols="3"] > .s4-cov__cell { border-left: 1px solid var(--line); border-top: 1px solid var(--line); }
  .s4-cov__list[data-cols="2"] > .s4-cov__cell:nth-child(2n + 1),
  .s4-cov__list[data-cols="3"] > .s4-cov__cell:nth-child(2n + 1) { border-left: 0; }
  .s4-cov__list[data-cols="2"] > .s4-cov__cell:nth-child(-n + 2),
  .s4-cov__list[data-cols="3"] > .s4-cov__cell:nth-child(-n + 2) { border-top: 0; }
  .s4-cov__cell { padding-block: 24px 28px; }
  .s4-cov__cell--feature { grid-column: span 2; }
  .s4-cov__cell--feature .s4-cov__glyph { width: 32px; height: 32px; }
  .s4-cov__cell--feature .s4-cov__title { font-size: 24px; }
  .s4-cov__cell--feature .s4-cov__text  { font: var(--t-body); max-width: 62ch; }
}
@media (min-width: 1024px) {
  .s4-cov__list { --cell-x: 24px; --cell-bleed: 24px; margin-top: var(--s-8); }
  .s4-cov__list[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* re-declared, because the rendered column count changed under it */
  .s4-cov__list[data-cols="3"] > .s4-cov__cell:nth-child(2n + 1) { border-left: 1px solid var(--line); }
  .s4-cov__list[data-cols="3"] > .s4-cov__cell:nth-child(3n + 1) { border-left: 0; }
  .s4-cov__list[data-cols="3"] > .s4-cov__cell:nth-child(-n + 3) { border-top: 0; }
  .s4-cov__cell { padding-block: 26px 30px; }
  .s4-cov__cell--void { display: grid; }
  .s4-cov__title { font-size: 21px; }
}
@media (min-width: 1180px) {
  .s4-cov__list { --cell-x: 28px; --cell-bleed: 28px; margin-top: var(--s-9); }
  .s4-cov__cell { padding-block: 28px 32px; }
  .s4-cov__title { font-size: 22px; }
}
@media (min-width: 1424px) {
  .s4-cov__list { --cell-x: 32px; --cell-bleed: 32px; }
  .s4-cov__cell { padding-block: 30px 34px; }
}

/* ========================================================================
   BAND 5 | HOW IT RUNS, THE DRAWN SPINE  (§4.5)
   The biggest picture on the page: a full-bleed --navy-050 plane carrying
   numbered nodes on a drawn rail that line-draws once on entry. It is the
   only drawn animation on the page.
   ===================================================================== */
.s4-runs                { padding-top: var(--band-y-lg); background: var(--paper); }
.s4-runs__intro         { margin-top: var(--s-5); font: var(--t-lead); color: var(--ink-3); max-width: 52ch; }
.s4-runs__intro > * + * { margin-top: var(--s-4); }

/* a DIRECT CHILD of the section, outside .container, so it is full bleed by
   structure: no 50vw arithmetic and no scrollbar rounding */
.s4-runs__plane {
  margin-top: var(--s-10);
  padding-block: var(--s-10);
  background: var(--navy-050);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.s4-runs__list {
  display: grid;
  grid-template-columns: repeat(var(--run-cols, 1), minmax(0, 1fr));
  column-gap: var(--run-gap, 0px);
  row-gap: var(--s-8);
  list-style: none;
}
.s4-run {
  position: relative;
  display: grid;
  grid-template-rows: auto auto auto;
  align-content: start;
}
.s4-run__dot {
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--navy);
}
/* the single gold terminal node, ringed in navy so the gold is never the only
   signal and never sits on a light ground unringed */
.s4-run.is-last .s4-run__dot { background: var(--gold); box-shadow: 0 0 0 2px var(--navy); }
.s4-run__num   { margin-top: var(--s-2); font: 600 13px/1 var(--font-display); font-variant-numeric: tabular-nums; letter-spacing: .06em; color: var(--navy-500); }
.s4-run__title { margin-top: var(--s-2); font: 600 20px/1.24 var(--font-display); letter-spacing: -.016em; color: var(--navy); }
.s4-run__text  { margin-top: 10px; font: var(--t-body-sm); color: var(--ink-2); }
.s4-run__text :is(p, li) + :is(p, li) { margin-top: var(--s-2); }
.s4-run__text :is(ul, ol) { padding-left: var(--s-5); list-style: disc outside; }

/* the rail, cut at the nodes: one segment inside each step's own cell,
   running from its 12px node to the next one. 1000 viewBox units stretched
   across the gap, preserveAspectRatio="none", and a --len written into the
   markup because getTotalLength() is forbidden at runtime. */
.s4-spine {
  display: none;
  position: absolute; top: 5px; left: 6px;
  height: 2px;
  /* from THIS cell's node centre to the NEXT one: one column plus one gap.
     max-width is reset because next.css clamps every svg to 100% of its
     container, which cut the rail 40px short of the node it points at. */
  width: calc(100% + var(--run-gap, 0px));
  max-width: none;
}
/* --navy-300 and not --navy-100: on a --navy-050 plane a --navy-100 rail is
   1.3:1 and simply is not there, which is the defect this band exists to fix.
   --navy-300 is 3.2:1 on the plane, the non-text UI floor, and the rail is
   decorative structure that is redundant with the numbered list under it. */
.s4-spine__rail { stroke: var(--navy-300); stroke-width: 2; fill: none; vector-effect: non-scaling-stroke; }

/* THE PHONE READS A SEQUENCE DOWN. Below 768 the horizontal rail is replaced
   by the shipped vertical geometry, verbatim, because that rule is
   known-good. */
@media (max-width: 767px) {
  .s4-run        { padding-left: var(--s-7); }
  .s4-run__dot   { position: absolute; left: 0; top: 4px; width: 10px; height: 10px; }
  .s4-run::after {
    content: ""; position: absolute; left: 4px; top: 18px;
    bottom: calc(var(--s-8) * -1); width: 2px; background: var(--navy-100);
    transform: scaleY(0); transform-origin: top;
  }
  .s4-runs__list.is-in .s4-run::after,
  html:not(.js) .s4-run::after { transform: scaleY(1); transition: transform 520ms var(--ease-soft) calc(var(--i, 0) * 90ms); }
  .s4-run.is-last::after { content: none; }
}
@media (min-width: 768px) {
  .s4-runs__list { --run-cols: 2; --run-gap: var(--s-7); }
  .s4-spine      { display: block; }
  .s4-runs__list[data-cols] > .s4-run:nth-child(2n) .s4-spine { display: none; }
}
@media (min-width: 1024px) {
  .s4-runs__plane { padding-block: var(--s-11); }
  .s4-runs__list[data-cols="2"] { --run-cols: 2; }
  .s4-runs__list[data-cols="3"],
  .s4-runs__list[data-cols="4"] { --run-cols: 3; }
  .s4-runs__list { --run-gap: var(--s-7); }
  .s4-runs__list[data-cols] > .s4-run:nth-child(2n) .s4-spine { display: block; }
  .s4-runs__list[data-cols="2"] > .s4-run:nth-child(2n) .s4-spine,
  .s4-runs__list[data-cols="3"] > .s4-run:nth-child(3n) .s4-spine,
  .s4-runs__list[data-cols="4"] > .s4-run:nth-child(3n) .s4-spine { display: none; }
  .s4-run__title { font-size: 19px; }
}
@media (min-width: 1180px) {
  .s4-runs__list { --run-gap: 36px; }
  .s4-runs__list[data-cols="4"] { --run-cols: 4; }
  .s4-runs__list[data-cols="4"] > .s4-run:nth-child(3n) .s4-spine { display: block; }
  .s4-runs__list[data-cols="4"] > .s4-run:nth-child(4n) .s4-spine { display: none; }
  .s4-run__title { font-size: 20px; }
}
@media (min-width: 1440px) {
  .s4-runs__list { --run-gap: var(--s-8); row-gap: var(--s-10); }
}
/* the last step never carries a segment at any width */
.s4-run.is-last .s4-spine { display: none; }

/* MOTION: the one line-draw on the page, and it fires once. */
.js .s4-spine__rail { stroke-dasharray: var(--len); stroke-dashoffset: var(--len); }
.js .s4-runs__list.is-in .s4-spine__rail {
  animation: s4-draw 1100ms var(--ease-soft) forwards;
  animation-delay: calc(240ms + var(--i, 0) * 70ms);
}
@keyframes s4-draw { to { stroke-dashoffset: 0; } }

.js .s4-run__dot { opacity: 0; transform: scale(.6); }
.js .s4-runs__list.is-in .s4-run__dot {
  animation: s4-pop 260ms var(--ease-out) forwards;
  animation-delay: calc(140ms + var(--i, 0) * 70ms);
}
@keyframes s4-pop { to { opacity: 1; transform: none; } }

html:not(.js) .s4-spine__rail { stroke-dasharray: none; stroke-dashoffset: 0; }
html:not(.js) .s4-run__dot    { opacity: 1; transform: none; }

/* ========================================================================
   BAND 6 | OUR APPROACH  (§4.6)
   Unchanged in substance: a numbered register that cannot be confused with
   the deliverables. The aside becomes a composed column and takes the
   pull-quote the derived sentence used to hide inside the intro.
   ===================================================================== */
.s4-why        { padding-block: var(--band-y); }
.s4-why--paper { background: var(--paper); }
.s4-why--wash  { background: var(--wash); }
.s4-why__grid  { display: grid; gap: var(--s-8); align-items: start; }
.s4-why__aside { max-width: none; }
.s4-why__intro { margin-top: var(--s-5); font: var(--t-lead); color: var(--ink-3); max-width: 44ch; }
.s4-why__intro > * + * { margin-top: var(--s-4); }
.s4-why__fig   { display: none; margin: 0; }
.s4-why__pull  { margin-top: var(--s-7); }
.s4-why__pull p {
  padding-left: var(--s-4);
  border-left: 3px solid var(--navy);
  font: 600 clamp(17px, .4vw + 15px, 20px)/1.45 var(--font-ui);
  color: var(--navy);
  max-width: 30ch;
}

.s4-why__reg {
  margin-top: var(--s-7);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.s4-why__row {
  display: grid;
  padding-block: 24px 26px;
  border-bottom: 1px solid var(--line);
}
.s4-why__row:last-child { border-bottom: 0; }
/* 12px, tabular, --ink-3 at 5.6:1: decoration, aria-hidden, and deliberately
   NOT the 44px pale numeral the client rejected on the other two bands */
.s4-why__num {
  font: 600 12px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: .08em;
  color: var(--ink-3);
}
.s4-why__term {
  margin: 10px 0 0;
  font: 600 21px/1.26 var(--font-display);
  letter-spacing: -.014em;
  color: var(--navy);
  text-wrap: balance;
}
/* FLUSH LEFT, margin-inline-start explicitly zeroed: <dd> carries a 40px UA
   indent and that indent was the oddly indented body copy on the old band */
.s4-why__desc {
  margin: 8px 0 0;
  font: var(--t-body-sm);
  color: var(--ink-2);
  max-width: 62ch;
}
.s4-why__desc :is(p, li) + :is(p, li) { margin-top: var(--s-2); }
.s4-why__desc :is(ul, ol) { padding-left: var(--s-5); list-style: disc outside; }

@media (min-width: 1024px) {
  .s4-why__grid { grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); column-gap: var(--s-10); }
  .s4-why__fig  {
    display: block; margin-top: var(--s-8);
    aspect-ratio: 3 / 2; border-radius: var(--r-md);
    border: 1px solid var(--line); background: var(--navy-050); overflow: clip;
  }
  .s4-why__fig :is(img, picture) { display: block; width: 100%; height: 100%; object-fit: cover; object-position: var(--po, 50% 44%); }
  .s4-why__row  { padding-block: 26px 30px; }
  .s4-why__term { font-size: 22px; }
}
@media (min-width: 1180px) {
  .s4-why__grid { column-gap: var(--s-11); }
  .s4-why__fig  { border-radius: var(--r-lg); }
  /* two-up ONLY on an even count (PHP writes data-cols), so the grid can never
     end on a lone cell beside a hole */
  .s4-why__reg[data-cols="2"] {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--s-10);
  }
  .s4-why__reg[data-cols="2"] > .s4-why__row:nth-last-child(-n + 2) { border-bottom: 0; }
}

/* ========================================================================
   BAND 7 | PROOF, THE PAGE'S ONE FULL-BLEED NAVY BAND  (§4.7)
   The only place on the page where somebody else speaks. Gold count in this
   band is three: the eyebrow tick, the + and % suffixes, and the case tile's
   eyebrow and metric marks at --gold-200. --gold on --navy is 9.2:1.
   ===================================================================== */
.s4-proof {
  position: relative; isolation: isolate;
  padding-block: var(--band-y-lg);
  background: linear-gradient(140deg, var(--navy-900) 0%, var(--navy) 58%, #04386F 100%);
  color: #fff;
}
.s4-proof__pattern {
  position: absolute; inset: 0; z-index: var(--z-pattern); pointer-events: none;
  background-image: var(--pat-grid-inv);
  -webkit-mask-image: radial-gradient(120% 90% at 88% 8%, #000 0, #000 42%, transparent 78%);
          mask-image: radial-gradient(120% 90% at 88% 8%, #000 0, #000 42%, transparent 78%);
}
.s4-proof > .container { position: relative; z-index: var(--z-content); }
.s4-proof .eyebrow     { color: rgba(255, 255, 255, .72); }
.s4-proof__h {
  margin-top: var(--s-4);
  font: var(--t-d2); letter-spacing: -.022em;
  color: #fff; max-width: 20ch; text-wrap: balance;
}

.s4-proof__stats { margin-top: var(--s-9); list-style: none; display: grid; gap: 0; border-top: 1px solid rgba(255, 255, 255, .18); }
.s4-stat    { padding-block: var(--s-6); border-bottom: 1px solid rgba(255, 255, 255, .18); }
.s4-stat__n { display: block; font: var(--t-num); font-variant-numeric: tabular-nums; letter-spacing: -.032em; color: #fff; }
.s4-stat__s { color: var(--gold); }
.s4-stat__l { display: block; margin-top: var(--s-2); font: var(--t-cap); color: rgba(255, 255, 255, .78); }

.s4-proof__grid { margin-top: var(--s-10); display: grid; gap: var(--s-6); align-items: start; }
/* a --paper card on a navy field is the highest-contrast pairing in the
   palette, which is why nothing in this band needs glass */
.s4-proof__quote { margin: 0; }

.s4-case {
  display: grid;
  align-content: start;
  padding: var(--s-6);
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: var(--r-lg);
  background: rgba(255, 255, 255, .06);
  text-decoration: none; color: #fff;
  transition: background-color var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out);
}
.s4-case:hover, .s4-case:focus-visible { background: rgba(255, 255, 255, .11); border-color: rgba(255, 255, 255, .44); }
.s4-case__k  { display: block; font: var(--t-eyebrow); text-transform: uppercase; letter-spacing: .14em; color: var(--gold-200); }
.s4-case__n  { display: block; margin-top: var(--s-3); font: 600 26px/1.2 var(--font-display); letter-spacing: -.018em; }
.s4-case__s  { display: block; margin-top: 2px; font: var(--t-cap); color: rgba(255, 255, 255, .72); }
.s4-case__m  { display: block; margin-top: var(--s-5); }
.s4-case__mi {
  display: grid; grid-template-columns: 22px minmax(0, 1fr); gap: var(--s-3);
  align-items: start; padding-block: var(--s-3);
  border-top: 1px solid rgba(255, 255, 255, .16);
  font: var(--t-body-sm); color: rgba(255, 255, 255, .88);
}
.s4-case__ico svg { display: block; width: 20px; height: 20px; color: var(--gold-200); }
.s4-case__go  { display: inline-flex; align-items: center; gap: var(--s-2); margin-top: var(--s-5); font: 600 15px/1.2 var(--font-ui); color: #fff; }
.s4-case__go svg { width: 16px; height: 16px; transition: transform var(--dur-1) var(--ease-out); }
.s4-case:hover .s4-case__go svg { transform: translateX(4px); }

@media (min-width: 768px) {
  .s4-proof__stats { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--s-8); border-bottom: 1px solid rgba(255, 255, 255, .18); }
  .s4-stat { border-bottom: 0; }
  .s4-stat + .s4-stat { border-left: 1px solid rgba(255, 255, 255, .18); padding-left: var(--s-8); }
}
@media (min-width: 1024px) {
  .s4-proof__grid { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); column-gap: var(--s-9); }
  /* with no published quote that names this page's discipline the tile takes
     the measure alone rather than sitting in a 5fr track beside a hole */
  .s4-proof__grid--solo { grid-template-columns: minmax(0, 1fr); }
  .s4-proof__grid--solo .s4-case { max-width: 720px; }
}
@media (min-width: 1180px) { .s4-proof__grid { column-gap: var(--s-10); } }
@media (max-width: 767px)  { .s4-proof__pattern { display: none; } }

/* ========================================================================
   BAND 8 | PLATFORMS, two deltas  (§4.8)
   The strip now sits directly under the navy band, where a --line hairline
   against that band's edge disappears, and its padding grows so the marquee
   is not crushed against it.
   ===================================================================== */
.svc-plat--r4 { border-top-color: var(--navy-100); border-bottom-color: var(--navy-100); }
@media (min-width: 1024px) { .svc-plat--r4 { padding-block: var(--s-11); } }
/* The marquee track is `width: max-content` inside an auto grid track, whose
   min-width: auto resolves to that same max-content, so the strip was pushing
   the document 2,000px wide at 390. .lgrail's own overflow: hidden clips the
   paint but cannot shrink the box. Scoped to the R4 strip: page-about renders
   the same component and is not in this commit. */
.svc-plat--r4 .svc-plat__grid > * { min-width: 0; }

/* ========================================================================
   BAND 9 | WHAT IT COSTS  (§4.9)
   The firm's own published ladder, drawn. It never issues a quote and it
   renders on 23 of 100 URLs. Shape D, centred narrow, left-aligned text.
   ===================================================================== */
.s4-price        { padding-block: var(--band-y); }
.s4-price--wash  { background: var(--wash); }
.s4-price--paper { background: var(--paper); }
.s4-price__in    { width: 100%; max-width: 780px; margin-inline: auto; }
.s4-price__head  { display: grid; gap: var(--s-6); align-items: start; }
.s4-price__head h2 {
  margin-top: var(--s-4);
  font: var(--t-d2); letter-spacing: -.022em; color: var(--ink);
}
.s4-price__art   { display: none; margin: 0; }
.s4-price__lead  { margin-top: var(--s-5); font: var(--t-lead); color: var(--ink-2); max-width: 54ch; }
.s4-price__ladder { margin-top: var(--s-9); }
.s4-price__bar   { display: block; width: 100%; height: 8px; }
.s4-price__seg   { fill: var(--navy-100); }
.s4-price__seg:nth-of-type(2) { fill: #C2D3E6; }
.s4-price__seg:nth-of-type(3) { fill: var(--navy-300); }
.s4-price__seg:nth-of-type(4) { fill: var(--navy-500); }
/* the key row shares the bar's four proportions, so the words sit under the
   segment they name at every width */
.s4-price__keys  { display: grid; grid-template-columns: 150fr 250fr 350fr 500fr; gap: 6px; margin-top: var(--s-3); }
.s4-price__key   { font: 600 12px/1.3 var(--font-ui); color: var(--ink-3); }
.s4-price__key b { display: block; font: 600 15px/1.3 var(--font-display); font-variant-numeric: tabular-nums; color: var(--navy); }
.s4-price__acts  { margin-top: var(--s-8); display: flex; flex-wrap: wrap; gap: var(--s-4); align-items: center; }
.s4-price__note  { margin-top: var(--s-4); font: var(--t-cap); color: var(--ink-3); }
@media (max-width: 480px) {
  .s4-price__keys { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--s-3); }
}
@media (min-width: 768px) {
  .s4-price__head { grid-template-columns: minmax(0, 1fr) 200px; column-gap: var(--s-9); }
  .s4-price__art  {
    display: block; width: 200px; aspect-ratio: 1 / 1;
    border-radius: var(--r-md); border: 1px solid var(--navy-100);
    background: var(--navy-050); overflow: clip;
  }
}
@media (min-width: 1024px) {
  .s4-price__head { grid-template-columns: minmax(0, 1fr) 220px; }
  .s4-price__art  { width: 220px; }
}

/* ========================================================================
   BAND 10 | QUESTIONS  (§4.10)
   The shipped accordion, unchanged in semantics and in keyboard behaviour,
   with a mapped mark per row and the illustration in the aside. The mark is
   aria-hidden and the summary's accessible name is byte-identical to R3's,
   so the FAQ schema and the accordion semantics do not move.
   ===================================================================== */
.s4-faq        { padding-block: var(--band-y); }
.s4-faq--paper { background: var(--paper); }
.s4-faq--wash  { background: var(--wash); }
.s4-faq__grid  { display: grid; gap: var(--s-9); align-items: start; }
.s4-faq__aside { max-width: none; }
.s4-faq__art   { display: none; margin: 0; }

.s4-faq__list { border-top: 1px solid var(--line); }
.s4-q         { position: relative; isolation: isolate; border-bottom: 1px solid var(--line); }
.s4-q__sum {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) 20px;
  gap: var(--s-4);
  align-items: start;
  min-height: 68px; padding-block: var(--s-5);
  cursor: pointer; list-style: none;
}
.s4-q__sum::-webkit-details-marker { display: none; }
.s4-q__ico svg {
  display: block; width: 22px; height: 22px; margin-top: 3px;
  color: var(--ink-3);
  transition: color var(--dur-1) var(--ease-out);
}
.s4-q[open] .s4-q__ico svg,
.s4-q__sum:hover .s4-q__ico svg { color: var(--navy); }
.s4-q__q { min-width: 0; font: 600 18px/1.4 var(--font-display); letter-spacing: -.010em; color: var(--navy); overflow-wrap: anywhere; }
.s4-q__sum:hover .s4-q__q { color: var(--navy-500); }
.s4-q__chev {
  width: 18px; height: 18px; margin-top: 5px; color: var(--navy-300);
  transition: transform var(--dur-2) var(--ease-io), color var(--dur-1) var(--ease-out);
}
.s4-q[open] .s4-q__chev { transform: rotate(180deg); color: var(--navy); }
.s4-q__sum:hover .s4-q__chev { color: var(--navy); }
.s4-q__sum:focus-visible { outline: 2px solid var(--navy); outline-offset: 3px; border-radius: var(--r-xs); }
/* the answer hangs under the QUESTION, not under the mark */
.s4-q__a   { padding-left: calc(24px + var(--s-4)); padding-bottom: var(--s-6); }
.s4-q__a p { font: var(--t-body); color: var(--ink-3); max-width: 62ch; }
.s4-q__a p + p { margin-top: var(--s-4); }
.s4-q__a :is(ul, ol) { padding-left: var(--s-5); list-style: disc outside; }
/* the open row: a fill bleeding 20px past the text edge with a 3px gold left
   edge on the same pseudo-element. Gold is an edge, never a fill, never behind
   text, and it appears exactly once in the band. */
.s4-q[open]::before {
  content: "";
  position: absolute; inset: 0 -20px;
  z-index: -1;
  background: var(--paper);
  border-radius: var(--r-sm);
  border-left: 3px solid var(--gold);
}
.s4-faq--paper .s4-q[open]::before { background: var(--navy-050); }
/* Progressive enhancement only. Where ::details-content does not exist the
   disclosure snaps, which is what <details> has always done. */
@supports selector(::details-content) {
  .s4-q::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size var(--dur-3) var(--ease-io), content-visibility var(--dur-3) allow-discrete;
  }
  .s4-q[open]::details-content { block-size: auto; }
}
@media (min-width: 1024px) {
  .s4-faq__grid { grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); column-gap: var(--s-10); }
  .s4-faq__aside .sec__intro { max-width: 34ch; }
  /* width is DEFINITE, not `100%` capped at 280. An aspect-ratio box whose
     width is a percentage contributes an indefinite height while its grid
     track is being sized, so the aside measured 39px shorter than its own
     content and the plate hung past the band's content edge. Every capped
     plate on both singles is declared the same way for the same reason. */
  .s4-faq__art {
    display: block; width: 280px; max-width: 100%; margin-top: var(--s-7);
    aspect-ratio: 1 / 1; border-radius: var(--r-md);
    border: 1px solid var(--navy-100); background: var(--navy-050); overflow: clip;
  }
}
@media (min-width: 1180px) { .s4-faq__grid { column-gap: var(--s-11); } }

/* ========================================================================
   BAND 11 | RELATED  (§4.11)
   Three link cards, each opened by a mark keyed to the target service's own
   title, so the three are distinguishable at a glance instead of three
   identical text blocks.
   ===================================================================== */
.s4-rel        { padding-block: var(--band-y); }
.s4-rel--paper { background: var(--paper); }
.s4-rel--wash  { background: var(--wash); }
.s4-rel__grid {
  margin-top: var(--s-8);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-5);
  list-style: none;
}
@media (min-width: 768px)  { .s4-rel__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-6); } }
@media (min-width: 1024px) { .s4-rel__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.s4-rel__card { display: grid; }
.s4-rel__link {
  display: grid;
  grid-template-rows: auto auto auto 1fr auto;
  height: 100%;
  padding: var(--s-6);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  text-decoration: none;
  transition: border-color var(--dur-2) var(--ease-out), background-color var(--dur-2) var(--ease-out);
}
.s4-rel__link:hover,
.s4-rel__link:focus-visible { background: var(--navy-050); border-color: var(--navy); }
.s4-rel__link:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; }
.s4-rel__ico     { display: block; width: 24px; height: 24px; color: var(--ink-3); transition: color var(--dur-2) var(--ease-out); }
.s4-rel__ico svg { display: block; width: 100%; height: 100%; }
.s4-rel__link:hover .s4-rel__ico,
.s4-rel__link:focus-visible .s4-rel__ico { color: var(--navy); }
.s4-rel__ix {
  margin-top: var(--s-4);
  font: 600 12px/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: .08em;
  color: var(--ink-3);
}
.s4-rel__name {
  margin-top: var(--s-3);
  font: 600 20px/1.26 var(--font-display);
  letter-spacing: -.014em;
  color: var(--navy);
  text-wrap: balance;
}
.s4-rel__desc { margin-top: 10px; font: var(--t-body-sm); color: var(--ink-2); }
.s4-rel__go {
  margin-top: var(--s-6);
  align-self: end;
  color: var(--ink-3);
  transition: color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.s4-rel__go svg { display: block; width: 16px; height: 16px; }
.s4-rel__link:hover .s4-rel__go,
.s4-rel__link:focus-visible .s4-rel__go { color: var(--navy); transform: translateX(4px); }
/* the "see all" tile only exists when fewer than three services resolved, and
   it is the same card on a tinted ground so it does not read as a service */
.s4-rel__card--all .s4-rel__link { background: var(--navy-050); }
.s4-rel__all { margin-top: var(--s-7); }

/* ========================================================================
   ANCHORS, NO-JS, REDUCED MOTION, HIGH CONTRAST
   ===================================================================== */
.page-service :is(#s4-fit, #s4-cov, #s4-runs, #s4-why, #s4-proof, #s4-price, #s4-faq) {
  scroll-margin-top: calc(var(--header-h, 80px) + var(--s-5));
}

/* .r is scoped to .js so content renders without it; .is-in never lands
   without the script, so the two animations that key off it rest finished. */
html:not(.js) .s4-runs__list .s4-spine__rail { stroke-dashoffset: 0; }

@media (prefers-reduced-motion: reduce) {
  .s4-spine__rail { stroke-dasharray: none !important; stroke-dashoffset: 0 !important; animation: none !important; }
  .s4-run__dot    { opacity: 1 !important; transform: none !important; animation: none !important; }
  .s4-run::after  { transform: scaleY(1) !important; transition: none !important; }
  .s4-rel__link:hover,
  .s4-case:hover  { transform: none !important; }
  .s4-cov__link:hover::after { transform: scaleY(1) !important; }
}

@media (prefers-contrast: more) {
  .s4-spine__rail { stroke: var(--navy-300); }
  .s4-fit__plate, .s4-cov__art, .s4-faq__art, .s4-price__art, .s4-open__plate, .s4-why__fig {
    outline: 1px solid var(--navy-100);
  }
  .s4-cov__cell, .s4-why__row, .s4-q { border-color: var(--navy-300); }
}

/* ===========================================================================
   ROUND FOUR, THE INDUSTRY SINGLE  (SPEC-IND-R4)
   templates/industry-next.php only. Eleven bands, the same objects as the
   service single in a different order.

   WHERE THIS FILE SAYS "SHARED", THE SERVICE BLOCK ABOVE IS THE DEFINITION
   and nothing is restated here. The register grid, the accordion, the proof
   band, the stat set, the case tile, the illustration mount, the index-bar
   mark and the contained close are all rendered by the .s4-* classes above;
   the industry markup carries the .i4-* class ALONGSIDE the .s4-* one, so the
   geometry has exactly one definition on the site and this block only has to
   say what is different. That is why the industry total below is 480 new lines
   and not 690: 210 of the spec's budget was a rename, and a rename of a shared
   object is a second copy of it.

   New here: the operating-model diagram (band 6), the 4/8 duo (band 4), the
   docket's head figure and term mark (band 7), the facts panel's mark (band
   2), and the services strip's mark (band 5).
   ======================================================================== */

/* --- band 1 · the hero's index bar --------------------------------------
   .ihero--ind drops the sub-line (the counts are the facts panel's job 180px
   below), so a cell is icon + numeral + label and the bar rests shorter than
   the service page's. */
.page-industry .ihero__cell .s4-ix__ico { margin-bottom: 2px; }

/* --- band 2 · the Opening Record, three additive deltas ------------------
   The band is NOT redesigned and that is a decision: it was rebuilt three
   times, and the third attempt already does what the owner is asking of the
   rest of the page. It has a picture, a panel, an authored headline and a
   rhythm. Redesigning it would be churn. */
.io__fact  { grid-template-columns: 22px 46px minmax(0, 1fr) 16px; }
.io__fico  { display: block; width: 20px; height: 20px; color: var(--navy-300); transition: color var(--dur-1) var(--ease-out); }
.io__fico svg { display: block; width: 100%; height: 100%; }
.io__fact:hover .io__fico { color: var(--navy); }

/* --- band 3 · pressure points --------------------------------------------
   The shared register (.s4-cov__*), on --wash, with a counted intro and a head
   figure. ind_pains has no `url` sub-field, so no cell here is ever an anchor
   and no cell has a hover state: a hover on a non-interactive row is a false
   affordance. */
.i4-press { padding-block: var(--band-y); }
.i4-press .sec__intro { max-width: 46ch; }

/* --- band 4 · what we do --------------------------------------------------
   The duo, object 3 of the shared vocabulary: two columns, row-major, ruled
   cells, NO NUMERALS ever, a <dl> with <div> row wrappers. R4 gives it an
   aside, and therefore a shape: eyebrow, h2 and the illustration in the left
   4, the duo in the right 8. That is what stops it reading as a second
   register directly under band 3.

   An ODD item count leaves the last cell alone in the left column with the
   right column empty. That is correct for a ruled grid and nothing fills it. */
.i4-work__grid  { display: grid; gap: var(--s-8); align-items: start; }
.i4-work__aside { max-width: none; }
.i4-work__art   {
  display: none;
  width: 260px; max-width: 100%;
  margin: var(--s-7) 0 0;
  aspect-ratio: 1 / 1;
  border-radius: var(--r-md);
  border: 1px solid var(--navy-100);
  background: var(--navy-050);
  overflow: clip;
}
@media (min-width: 561px) { .i4-work__art { display: block; } }
@media (min-width: 768px) { .i4-work__art { width: 300px; } }
@media (min-width: 1024px) {
  .i4-work__grid { grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); column-gap: var(--s-10); }
  .i4-work__list { margin-top: 0; }
}
@media (min-width: 1180px) {
  .i4-work__grid { column-gap: var(--s-11); }
  .i4-work__art  { width: 320px; }
}
@media (min-width: 1440px) { .i4-work__art { width: 340px; } }

/* --- band 5 · the services strip ----------------------------------------
   Six links to published service pages are the highest-intent links on the
   page. One 20px mark each, from the same map every other band uses, so they
   are distinguishable at a glance instead of six identical text rows. */
.indsvc__list a  { grid-template-columns: 22px minmax(0, 1fr) 16px; }
.indsvc__ico     { display: block; width: 20px; height: 20px; color: var(--ink-3); transition: color var(--dur-1) var(--ease-out); }
.indsvc__ico svg { display: block; width: 100%; height: 100%; }
.indsvc__list a:hover .indsvc__ico,
.indsvc__list a:focus-visible .indsvc__ico { color: var(--navy); }

/* ========================================================================
   BAND 6 | HOW THE WORK RUNS HERE, THE OPERATING MODEL  (§4.6) — NEW
   The band this page has never had. Five stations, drawn, with three of them
   fed from this sector's own fields. It is the page's second full-bleed
   tinted plane and the mirror of the service timeline: same plane, same
   ground, same drawn stroke, a different machine.

   Nothing on the diagram is a promise, a timeline, a price, a service level
   or an outcome. It is five nouns and five descriptions, three of which are
   counted off the page the reader is on.
   ===================================================================== */
.i4-model        { padding-top: var(--band-y-lg); background: var(--paper); }
.i4-model__plane {
  margin-top: var(--s-10);
  padding-block: var(--s-10);
  background: var(--navy-050);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.i4-model__wrap  { position: relative; }
.i4-model__svg   { display: none; }
.i4-node { fill: var(--paper); stroke: var(--navy-100); stroke-width: 1; vector-effect: non-scaling-stroke; }
.i4-link { stroke: var(--navy-300); stroke-width: 2; fill: none; vector-effect: non-scaling-stroke; }

.i4-model__list { list-style: none; display: grid; gap: var(--s-6); position: relative; z-index: 1; }
.i4-model__st {
  position: relative;
  padding: var(--s-5);
  border: 1px solid var(--navy-100);
  border-radius: var(--r-md);
  background: var(--paper);
}
.i4-model__ico svg { display: block; width: 24px; height: 24px; color: var(--navy); }
.i4-model__k {
  display: block; margin-top: var(--s-4);
  font: 600 15px/1.2 var(--font-display);
  letter-spacing: .08em; text-transform: uppercase; color: var(--navy);
}
.i4-model__g { display: block; margin-top: var(--s-2); font: 400 14px/1.5 var(--font-ui); color: var(--ink-2); }

/* THE MOBILE DIAGRAM IS A REAL LADDER, not a squashed row. Below 1024 the SVG
   is not rendered at all and each station draws its own box, with a 2px
   connector between rows and a chevron pointing down. The arrowheads are CSS
   border triangles at every width: a preserveAspectRatio="none" SVG cannot
   carry an arrowhead without deforming it. */
@media (max-width: 1023px) {
  .i4-model__list { gap: var(--s-9); }
  .i4-model__st + .i4-model__st::before {
    content: ""; position: absolute; left: 50%; top: calc(var(--s-9) * -1);
    width: 2px; height: var(--s-9); background: var(--navy-100); transform: translateX(-1px);
  }
  .i4-model__st + .i4-model__st::after {
    content: ""; position: absolute; left: 50%; top: -11px;
    border: 5px solid transparent; border-top-color: var(--navy-300); transform: translateX(-5px);
  }
}

/* THE DRAWN ROW, >=1024. The 176/30 rhythm in the viewBox is 17.6% + a 3% gap,
   which is exactly what `repeat(5, 1fr)` with `column-gap: 3%` resolves to, so
   the five drawn rectangles land under the five HTML stations at every width
   without a single magic number. */
@media (min-width: 1024px) {
  .i4-model__plane { padding-block: var(--s-11); }
  /* The wrap is .container, so `inset: 0` would stretch the drawing across the
     container's PADDING box and hang the five rectangles one gutter wider than
     the five HTML stations inside its content box. Inset by the gutter and the
     drawing and the copy share one edge at every width. */
  .i4-model__svg   {
    display: block; position: absolute;
    top: 0; left: var(--gutter);
    width: calc(100% - var(--gutter) * 2); height: 100%; max-width: none;
  }
  /* row-gap, not the `gap` shorthand: `gap: 0` after `column-gap: 3%` resets
     the column gap to zero and the five HTML stations then sit 31px wider than
     the five drawn rectangles by the end of the row. */
  .i4-model__list  { grid-template-columns: repeat(5, minmax(0, 1fr)); row-gap: 0; column-gap: 3%; }
  .i4-model__st    { border: 0; background: transparent; padding: var(--s-5); min-height: 96px; }
  .i4-model__st + .i4-model__st::after {
    content: ""; position: absolute; left: calc(-1.5% - 5px); top: 50%;
    border: 5px solid transparent; border-left-color: var(--navy-300); transform: translateY(-5px);
  }
}
@media (min-width: 1180px) {
  .i4-model__ico svg { width: 28px; height: 28px; }
  .i4-model__k { font-size: 16px; }
  .i4-model__g { font-size: 15px; }
}

/* motion: the four connector segments draw once, left to right. The :has()
   form is preferred and the sibling form is the fallback for engines without
   it; both target the same element and only one fires. Where neither is
   supported the connectors rest drawn, which is also the no-JS state. */
.js .i4-link { stroke-dasharray: var(--len); stroke-dashoffset: var(--len); }
.js .i4-model__list.is-in ~ .i4-model__svg .i4-link,
.js .i4-model__wrap:has(.is-in) .i4-link { animation: s4-draw 900ms var(--ease-soft) 200ms forwards; }
html:not(.js) .i4-link { stroke-dasharray: none; stroke-dashoffset: 0; }

/* ========================================================================
   BAND 7 | THE RULES YOU REPORT AGAINST  (§4.7)
   The docket, object 4: auto-fit minmax(220px, 1fr), the tightest pitch on
   the page. Two R4 deltas: the head goes 2-up with an illustration, and the
   drawn mark moves to the TERM's line so a 220px cell never wraps its term
   under its own icon.
   ===================================================================== */
.i4-std__head { max-width: none; display: grid; gap: var(--s-6); }
.i4-std__art  { display: none; margin: 0; }
@media (min-width: 1024px) {
  .i4-std__head { grid-template-columns: minmax(0, 1fr) 240px; column-gap: var(--s-9); align-items: start; }
  .i4-std__art  {
    display: block; width: 240px; aspect-ratio: 1 / 1;
    border-radius: var(--r-md); border: 1px solid var(--navy-100);
    background: var(--navy-050); overflow: clip;
  }
}
/* the cell gains a top rule and the term gains the mark */
.i4-std__item { border-top: 1px solid var(--navy-100); }
.i4-std__term {
  display: grid; grid-template-columns: 22px minmax(0, 1fr);
  column-gap: 10px; align-items: center;
}
.i4-std__ico { display: block; width: 22px; height: 22px; color: var(--navy-300); }
.i4-std__ico svg { display: block; width: 100%; height: 100%; }
/* the gloss now hangs at the cell's full width rather than under the mark */
.i4-std__gloss { grid-template-columns: minmax(0, 1fr); column-gap: 0; }

/* --- band 8 · proof ------------------------------------------------------
   The shared navy band, .s4-proof, rendered with .i4-proof alongside it. The
   only industry delta is the eight sectors that carry no published quote: the
   grid has one track and the tile takes the measure, which the shared
   --solo modifier above already does. */

/* --- band 10 · nearby sectors -------------------------------------------
   One delta: the band sits quieter than the navy band two above it. */
.indrel { padding-block: var(--band-y); }

/* --- anchors, reduced motion, high contrast ----------------------------- */
.page-industry :is(#i4-press, #i4-work, #ind-svc, #i4-model, #i4-std, #i4-proof, #i4-faq) {
  scroll-margin-top: calc(var(--header-h, 80px) + var(--s-5));
}
@media (prefers-reduced-motion: reduce) {
  .i4-link { stroke-dasharray: none !important; stroke-dashoffset: 0 !important; animation: none !important; }
}
@media (prefers-contrast: more) {
  .i4-node, .i4-work__ico { stroke: var(--navy-300); color: var(--navy); }
  .i4-work__art, .i4-std__art, .i4-model__st { outline: 1px solid var(--navy-100); }
}


/* =========================================================================
   MOBILE-QA-2026-09-20 — phone corrections for the interior templates.
   Appended as one block: type floors and hit area, no layout changes.
   ========================================================================= */
@media (max-width: 767px) {

  /* The industry overlay's secondary action is a full-width centred text
     link below the gold button (the block above already stacks them), but
     it was still only 24px tall. Vertical padding makes the row a 44px
     target without moving the text off the button's baseline rhythm. */
  .io__over-alt { display: block; padding-block: 10px; }

  /* Literal sub-13px values that the global --t-micro / --t-eyebrow floor
     in next.css does not reach. */
  .i4-std__note  { font-size: 13px; }
  .s4-price__key { font-size: 13px; }
}
