  :root{--prussian:#030F26;--imperial:#082767;--pacific:#00A1AF;--porcelain:#F7F7F3;--green:#A0CC3A;
    /* Two colours were repeated all over the stylesheet without ever being
       named: the hero stage navy (9 uses) and the pale teal the eyebrows and
       key swatches use (6). A brand colour written out by hand in nine places
       is nine places to miss when it changes. rgba() values are deliberately
       NOT tokenised — carrying alpha would need relative-colour syntax that
       is too new to put in front of a launch. */
    --stage:#04173B;--mint:#7FD0D7;
    /* The fixed header's own height: 14px padding + a 68px cap. It condenses
       to 9 + 58 = 76px past 40px of scroll, so reserving the tall state is
       always enough. Anything pinned and vertically centred must subtract
       this, or it centres against the viewport and slides under the nav. */
    --head-h:82px;
    /* ── CORNER SCALE ────────────────────────────────────────────────
       Eight distinct radii had accumulated — 3, 10, 14, 16, 20, 22, 26 and the
       pill — and they were not eight decisions. Two families were each doing
       one job at two or three sizes: photographic tiles at 14 and 16, panels
       at 20, 22 and 26. Nobody can name the difference between a 20px and a
       22px corner, but a stack card at 22 above a mosaic plate at 14 beside a
       panel at 20 reads as unresolved. Four steps, each with a job:
         --r-xs  hairline chrome (a legend swatch)
         --r-sm  small floating chrome (the map tooltip)
         --r-md  anything holding a photograph
         --r-lg  anything that is a panel or a card
       Circles stay 50% and pills stay 999px; both are shapes, not sizes. */
    --r-xs:3px;--r-sm:12px;--r-md:16px;--r-lg:22px;--r-pill:999px;
    /* ── TYPE SCALE ──────────────────────────────────────────────────
       Only the ramps that were the SAME SIZE written differently are merged
       here. The homepage carried clamp(38px,5.1vw,74px) and clamp(37px,5.1vw,
       73px) in adjacent rules — a one-pixel difference nobody chose — and
       three overlay ledes sat at 19px, 18.5px and 18px max. Two of those three
       were mine, added this week.
       Ramps that differ for a reason keep their own values and are NOT in this
       block: the parallax opener is smaller than the sequenced one because its
       stage is 250px shorter, and the Leadership statement is larger than
       everything because it is the only thing on a white screen. */
    --t-stmt:clamp(38px,5.1vw,74px);   /* homepage statements */
    /* ONE H1 RAMP (audit, 13 Aug 2026). Five were in use: 84px on the standard
       masthead, 84 on the expanding and sequenced openers but from a different
       floor, 96 on Leadership's statement and 72 on the market parallax. At
       390px that spread read 34 / 36 / 38 for the same element. Home keeps its
       own ramp because the scrollytelling opener is a different animal; every
       page h1 now derives from here. */
    --t-h1:clamp(38px,6vw,84px);
    /* ONE BODY LEADING. The size was already near-constant at 16.5px but the
       leading was not: 1.6, 1.62, 1.66 and 1.68 rendered as 26.4 / 26.73 /
       27.39 / 27.72px on different pages. A four-value spread that small does
       not read as a decision, it reads as drift. */
    --lh-body:1.64;
    /* ── SECTION RHYTHM ──────────────────────────────────────────────
       Named rather than renumbered. Measured across the interior pages the
       values were already one system — 63/63 on every body section, 63/81 on
       the closing CTA, and the masthead's header clearance on top — so the
       audit's "four systems" was counting the homepage's pinned sections
       alongside them. Those are load-bearing: #record, #services and
       #leadership are tuned against the ground journey's stop table and the
       pin heights, and re-numbering them moves the colour crossings. They keep
       their own values deliberately.
       What was missing was a NAME. Three literals repeated down the file drift
       the first time someone nudges one; three tokens cannot. */
    --sp-sec:clamp(48px,7vh,86px);   /* every interior body section */
    --sp-close:clamp(64px,9vh,110px);/* the closing CTA, before the footer */
    --sp-open:clamp(36px,5vh,64px);  /* masthead clearance under the fixed cap */
    --sp-open-b:clamp(74px,10vh,118px);
    /* ── CONTROLS ────────────────────────────────────────────────────
       Measured, the buttons were already systematic BY ROLE rather than
       inconsistent: one page pill (.pill), one nav pill (.cta), one filter
       chip (.lc-chip on the map) and a set of icon-only controls. The audit
       counted them together and read four sizes as drift.
       The one real accident was the pair: 15.5px on the page pill and 14.5px
       on the nav pill, a single pixel apart for no reason anyone chose — the
       same fault as the homepage's 37/38px headline twins. Both now read from
       --t-btn. The chip stays smaller because a filter is not a call to
       action, and that is a decision rather than a leftover. */
    --t-btn:15.5px;
    /* ── MOTION ──────────────────────────────────────────────────────
       Three durations for interaction, not twenty-three. .28s was already
       carrying 71 of them; the rest were a long tail of single uses within a
       few hundredths of each other — .18, .2, .25, .3, .34, .35, .38 all doing
       the same job at almost the same speed, which is what made similar
       elements feel subtly out of step.
       ANIMATION timings are untouched. Those are ten values in the sequenced
       hero's beat table, timed to each other and released by script; they are
       choreography, not drift, and snapping them to a scale would desynchronise
       the deal from the reveal. The same goes for the three deliberate long
       moves (.9s, 1.15s, 1.4s) on the drawer and the ground crossing. */
    --mo-ui:.28s;      /* hover, colour, focus — anything answering a pointer */
    --mo-move:.55s;    /* something changing place or size */
    --mo-slow:.8s;     /* a large, deliberate move */
    --t-h2:clamp(26px,3.4vw,46px);     /* section heading */
    --t-h3:clamp(26px,3.2vw,44px);     /* sub-heading */
    --t-lede:clamp(15px,1.5vw,19px)}   /* lede over a photograph */
  *{box-sizing:border-box}
  /* Form controls do not inherit type from their ancestors — that is a browser
     default, not a choice — so every <button> on the site was rendering in
     Arial 13.33px. Mostly invisible, because most of them hold an icon, but
     the About accordion triggers hold real text and they were the only
     non-brand font anywhere on the site. */
  button,input,select,textarea{font-family:inherit}
  /* Only the root carries a colour, as a fallback before the script runs.
     body MUST stay transparent — an in-flow block background paints AFTER
     negative-z-index children, so a colour here covers #ground entirely. */
  html{background:var(--prussian)}
  body{margin:0;background:transparent}
  /* One fixed layer under everything. Its colour is interpolated from document
     scroll every frame — continuous, not switched at section boundaries. Stop
     halfway between two stops and it sits halfway between the colours. */
  #ground{position:fixed;inset:0;z-index:-1;background:var(--prussian)}
  body{font-family:'DM Sans',system-ui,-apple-system,sans-serif;-webkit-font-smoothing:antialiased}

  /* ── header ───────────────────────────────────────────────────────── */
  header{position:fixed;top:0;left:0;right:0;z-index:40;padding:14px clamp(12px,3vw,26px);
    color:var(--porcelain);opacity:0;transform:translateY(-14px);
    transition:opacity var(--mo-slow) ease,transform var(--mo-slow) cubic-bezier(.22,1,.36,1),padding var(--mo-move) ease,color var(--mo-ui) ease,text-shadow var(--mo-ui) ease;
    text-shadow:0 1px 14px rgba(3,15,38,.55);
    /* ── KEEP IT ON ITS OWN LAYER (client, 14 Aug 2026: on a phone the menu
       "disappears when you scroll down, reappears at the bottom, and is there
       when you scroll upwards") ─────────────────────────────────────────
       That description is the signature of a compositing drop, not of anything
       this theme decides: lc-nav.js calls showNav(true) unconditionally on
       every frame, so nothing here ever asks the header to leave, and it was
       measured present with opacity 1 and no transform at all 41 sampled
       scroll positions.

       What it actually is: .capbg carries backdrop-filter INSIDE a
       position:fixed parent, and WebKit on device drops that layer during
       momentum scroll, repainting it only when the scroll settles or reverses
       — which is exactly "gone going down, back at the bottom, back on the way
       up". It does not reproduce in headless WebKit, so it cannot be caught
       here; these three lines are the standard hardening for it.

       translateZ(0) is NOT usable on this element — showNav writes `transform`
       inline and would erase it — so the promotion is done with will-change and
       backface-visibility, which survive the inline write. */
    will-change:opacity,transform;
    -webkit-backface-visibility:hidden;backface-visibility:hidden}
  /* THE CAP IS NOT ALWAYS PORCELAIN. It was, and on every interior page that
     put Porcelain nav links on the Porcelain ground those pages open on —
     1.00:1, invisible, not merely low-contrast. lc-nav.js now derives the ink
     from the same ground luminance the body type uses, but it cannot run
     before first paint, and it does not run at all without JS. So the default
     below is per-template: the homepage opens over the dark hero and keeps
     Porcelain; everything else opens on light ground and starts dark. Without
     this the correct-but-late swap is a visible flash on every page load. */
  body:not(.lc-front) header{color:var(--imperial);
    text-shadow:0 1px 10px rgba(247,247,243,.75)}
  .cap{position:relative;width:100%;max-width:1680px;margin:0 auto;padding:0 8px 0 20px;height:68px;
    display:flex;align-items:center;justify-content:space-between;gap:28px;border-radius:var(--r-pill);
    transition:max-width .9s cubic-bezier(.22,1,.36,1),height var(--mo-move) cubic-bezier(.22,1,.36,1)}
  /* The backdrop-filter layer itself is the one that drops — see the header
     note above. It is not the fixed element, so it CAN take translateZ(0),
     which pins it to its own composited layer and is the half of the fix that
     does the work. The header's will-change is the other half. */
  .capbg{position:absolute;inset:0;border-radius:var(--r-pill);opacity:0;
    background:rgba(3,15,38,.72);backdrop-filter:blur(18px) saturate(1.3);
    -webkit-backdrop-filter:blur(18px) saturate(1.3);
    border:1px solid rgba(247,247,243,.14);box-shadow:0 18px 46px rgba(3,15,38,.34);
    transform:translateZ(0);will-change:opacity;
    transition:opacity var(--mo-slow) cubic-bezier(.22,1,.36,1)}
  /* supplied mark is 855x540, so the box follows its 1.58 aspect exactly —
     the placeholder was 3.92 and stretched the wordmark out sideways */
  .logo{position:relative;z-index:1;display:block;width:79px;height:50px;flex:none;
    transition:width var(--mo-move) cubic-bezier(.22,1,.36,1),height var(--mo-move) cubic-bezier(.22,1,.36,1)}
  .logo img{width:100%;height:100%;object-fit:contain;object-position:left center;display:block}
  .cap nav{position:relative;z-index:1;flex:1;display:flex;align-items:center;justify-content:center;
    gap:22px;white-space:nowrap;font-size:14.5px;font-weight:600}
  .cap nav a{color:inherit;text-decoration:none;transition:color var(--mo-ui) ease}
  .cap nav a:hover{color:var(--pacific)}

  /* ── the markets dropdown ──────────────────────────────────────────
     NO SCRIPT. :hover opens it for a pointer and :focus-within opens it for a
     keyboard, which is the whole interaction — a menu that only needs to appear
     and disappear does not need state in JS, and this one has to work before
     lc-nav.js has run.

     THE PANEL PAINTS ITS OWN DARK SURFACE and pins its ink to match, because
     the cap's colour is driven by the ground underneath it and flips between
     Imperial and Porcelain as the page scrolls. Inheriting would put navy type
     on a navy plate exactly where the page happens to be light — the same fault
     that made the nav 1.00:1 and hid the inquiry form's submit button.

     padding-top on the panel rather than a margin: it bridges the gap between
     the link and the plate so the pointer can travel down without crossing dead
     space and closing the menu. */
  .cap nav .navdd{position:relative;display:block}
  .cap nav .navdd-panel{position:absolute;left:50%;top:100%;translate:-50% 0;
    padding-top:14px;display:grid;gap:2px;min-width:250px;
    opacity:0;visibility:hidden;transform:translateY(-6px);
    transition:opacity var(--mo-ui) ease,transform var(--mo-ui) ease,visibility var(--mo-ui);
    z-index:3}
  .cap nav .navdd:hover .navdd-panel,
  .cap nav .navdd:focus-within .navdd-panel{opacity:1;visibility:visible;transform:none}
  .cap nav .navdd-panel::before{content:'';position:absolute;inset:14px 0 0;
    border-radius:var(--r-md);background:rgba(3,15,38,.94);
    backdrop-filter:blur(18px) saturate(1.3);-webkit-backdrop-filter:blur(18px) saturate(1.3);
    border:1px solid rgba(247,247,243,.14);box-shadow:0 22px 54px rgba(3,15,38,.42)}
  .cap nav .navdd-panel a{position:relative;z-index:1;display:block;
    padding:11px 18px;font-size:14px;font-weight:600;line-height:1.3;
    color:rgba(247,247,243,.86);text-shadow:none;border-radius:var(--r-sm);
    transition:background var(--mo-ui) ease,color var(--mo-ui) ease}
  .cap nav .navdd-panel a:first-of-type{margin-top:8px}
  .cap nav .navdd-panel a:last-of-type{margin-bottom:8px}
  .cap nav .navdd-panel a:hover,
  .cap nav .navdd-panel a:focus-visible{background:rgba(247,247,243,.09);color:#fff}
  @media (prefers-reduced-motion:reduce){
    .cap nav .navdd-panel{transition:opacity var(--mo-ui) ease,visibility var(--mo-ui);transform:none}
  }
  /* #00838F rather than Pacific: white on Pacific measures 3.13:1 and this
     label is 14.5px bold, which needs 4.5:1. The darker teal measures 4.52:1
     and is visually within a hair of it. */
  .cta{position:relative;z-index:1;flex:none;background:#00838F;color:#fff;padding:10px 22px;
    border-radius:var(--r-pill);font-weight:700;font-size:var(--t-btn);text-decoration:none;transition:background var(--mo-ui) ease}
  .cta:hover{background:#00767F}
  /* `inherit`, not Porcelain. It stands in for the nav links on a phone, so it
     has to take the same ink they do — pinned to Porcelain it went invisible
     on light ground in exactly the same way, and only on mobile, where it
     would have been the last thing anyone checked. */
  .burger{position:relative;z-index:1;display:none;flex-direction:column;gap:5px;background:none;
    border:none;cursor:pointer;padding:10px;color:inherit}
  .burger span{display:block;width:26px;height:2px;background:currentColor;
    transition:transform var(--mo-ui) cubic-bezier(.22,1,.36,1),opacity var(--mo-ui) ease}
  /* ── mobile drawer ─────────────────────────────────────────────────
     A right-edge panel rather than a plate dropped under the cap. Three
     reasons it changed shape: full height gives the six links room to be
     read rather than scanned; the panel covers the burger once open, so it
     needs its own way out; and the office and contact details belong here,
     because on a phone the footer is a very long way down.
     The flourish is the green leading edge on each row — the same seam the
     Leadership assembly used before it was cut. Spent here it marks where
     you are instead of decorating, which is the only reason to keep it. */
  #mscrim{position:fixed;inset:0;z-index:44;background:rgba(3,15,38,.55);
    opacity:0;pointer-events:none;transition:opacity var(--mo-move) ease;
    backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px)}
  #mnav{position:fixed;z-index:45;top:0;right:0;bottom:0;width:min(100%,420px);
    display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:0;
    text-align:left;white-space:normal;
    background:linear-gradient(180deg,rgba(8,22,48,.97),rgba(3,15,38,.985));
    backdrop-filter:blur(22px) saturate(1.25);-webkit-backdrop-filter:blur(22px) saturate(1.25);
    border-left:1px solid rgba(247,247,243,.12);
    box-shadow:-30px 0 80px rgba(3,15,38,.55);
    transform:translateX(100%);pointer-events:none;visibility:hidden;
    transition:transform var(--mo-move) cubic-bezier(.22,1,.36,1),
      visibility 0s linear var(--mo-move);
    padding-bottom:max(18px,env(safe-area-inset-bottom))}
  /* visibility, not just the transform. Parked off-screen the drawer was still
     rendered, so its seventeen links stayed in the tab order: below 1081px a
     keyboard user left the burger and spent seventeen presses on things they
     could not see. pointer-events:none never covered that — it stops the
     mouse, not Tab. visibility:hidden takes the subtree out of the tab order,
     and the 0s delay holds it until the slide-out has finished so the close
     still animates. */
  html[data-menu="1"] #mnav{transform:none;pointer-events:auto;visibility:visible;
    transition:transform var(--mo-move) cubic-bezier(.22,1,.36,1),visibility 0s}
  html[data-menu="1"] #mscrim{opacity:1;pointer-events:auto}

  #mnav .mhead{flex:none;display:flex;align-items:center;justify-content:space-between;
    gap:16px;padding:18px 20px 16px;border-bottom:1px solid rgba(247,247,243,.10)}
  #mnav .mhead img{width:76px;height:48px;object-fit:contain;object-position:left center;display:block}
  #mnav .mclose{appearance:none;-webkit-appearance:none;background:none;
    border:1px solid rgba(247,247,243,.18);color:var(--porcelain);width:42px;height:42px;
    border-radius:var(--r-pill);cursor:pointer;flex:none;display:flex;align-items:center;
    justify-content:center;transition:background var(--mo-ui) ease,border-color var(--mo-ui) ease}
  #mnav .mclose:hover,#mnav .mclose:focus-visible{background:rgba(247,247,243,.10);
    border-color:rgba(247,247,243,.4)}
  #mnav .mclose svg{width:16px;height:16px;fill:none;stroke:currentColor;
    stroke-width:1.8;stroke-linecap:round}

  /* the link list is the only part that scrolls, so a short phone never
     pushes the contact details or the CTA off the bottom */
  #mnav .mscroll{flex:1 1 auto;min-height:0;overflow-y:auto;
    -webkit-overflow-scrolling:touch;padding:6px 0 2px}
  #mnav .mlink{position:relative;display:block;color:var(--porcelain);text-decoration:none;
    font-size:21px;font-weight:600;letter-spacing:-.02em;padding:15px 22px;
    border-bottom:1px solid rgba(247,247,243,.07);
    transition:background var(--mo-ui) ease,padding-left var(--mo-ui) cubic-bezier(.22,1,.36,1)}
  #mnav .mlink::before{content:'';position:absolute;left:0;top:14%;bottom:14%;width:2px;
    background:var(--green);transform:scaleY(0);
    transition:transform var(--mo-ui) cubic-bezier(.22,1,.36,1)}
  #mnav .mlink:hover,#mnav .mlink:focus-visible{background:rgba(247,247,243,.05);
    color:var(--porcelain);padding-left:28px}
  #mnav .mlink:hover::before,#mnav .mlink:focus-visible::before{transform:scaleY(1)}
  /* The six markets, indented under What We Do. Smaller and dimmer so the row
     reads as belonging to the link above it rather than as six more top-level
     destinations — but the same 15px padding block, so every one is still a
     52px tap target. */
  #mnav .mlink--sub{font-size:16px;font-weight:500;padding-left:38px;
    color:rgba(247,247,243,.72)}
  #mnav .mlink--sub::before{left:22px}
  #mnav .mlink--sub:hover,#mnav .mlink--sub:focus-visible{padding-left:44px;
    color:var(--porcelain)}

  #mnav .mfoot{flex:none;padding:20px 22px 0;border-top:1px solid rgba(247,247,243,.10)}
  #mnav .mgroup{margin:0 0 16px}
  #mnav .mlabel{margin:0 0 7px;font-size:11px;font-weight:700;letter-spacing:.14em;
    text-transform:uppercase;color:var(--mint)}
  #mnav .mfoot p{margin:0;font-size:14.5px;line-height:1.6;color:rgba(247,247,243,.66)}
  #mnav .mfoot .mline{display:block;font-size:14.5px;line-height:1.75;
    color:rgba(247,247,243,.66);text-decoration:none;transition:color var(--mo-ui) ease}
  #mnav .mfoot .mline:hover,#mnav .mfoot .mline:focus-visible{color:var(--porcelain)}
  #mnav .mcta{display:block;margin:2px 0 0;background:var(--mint);color:#062A33;
    text-align:center;padding:15px;border-radius:var(--r-pill);font-weight:700;font-size:15px;
    text-decoration:none;transition:background var(--mo-ui) ease}
  #mnav .mcta:hover,#mnav .mcta:focus-visible{background:#9BDDE2;color:#062A33}

  /* Rows arrive after the panel, not with it — the drawer lands first, then
     the contents settle into it. */
  #mnav .stag{opacity:0;transform:translateX(22px);
    transition:opacity var(--mo-ui) ease,transform var(--mo-move) cubic-bezier(.22,1,.36,1)}
  html[data-menu="1"] #mnav .stag{opacity:1;transform:none}
  /* TWELVE ROWS NOW, NOT SIX. The markets stack under What We Do in the drawer,
     and the stagger is enumerated because CSS cannot count — six rules covered
     six links, so the new ones would have arrived with no delay at all while
     the originals were still fading in. The step tightens from 45ms to 26ms so
     twelve rows finish in about the time six used to. */
  html[data-menu="1"] #mnav .mlink:nth-child(1){transition-delay:.140s}
  html[data-menu="1"] #mnav .mlink:nth-child(2){transition-delay:.166s}
  html[data-menu="1"] #mnav .mlink:nth-child(3){transition-delay:.192s}
  html[data-menu="1"] #mnav .mlink:nth-child(4){transition-delay:.218s}
  html[data-menu="1"] #mnav .mlink:nth-child(5){transition-delay:.244s}
  html[data-menu="1"] #mnav .mlink:nth-child(6){transition-delay:.270s}
  html[data-menu="1"] #mnav .mlink:nth-child(7){transition-delay:.296s}
  html[data-menu="1"] #mnav .mlink:nth-child(8){transition-delay:.322s}
  html[data-menu="1"] #mnav .mlink:nth-child(9){transition-delay:.348s}
  html[data-menu="1"] #mnav .mlink:nth-child(10){transition-delay:.374s}
  html[data-menu="1"] #mnav .mlink:nth-child(11){transition-delay:.400s}
  html[data-menu="1"] #mnav .mlink:nth-child(12){transition-delay:.426s}
  html[data-menu="1"] #mnav .mgroup:nth-of-type(1){transition-delay:.46s}
  html[data-menu="1"] #mnav .mgroup:nth-of-type(2){transition-delay:.50s}
  html[data-menu="1"] #mnav .mcta{transition-delay:.54s}
  /* the delays must not run in reverse on the way out, or the panel slides
     away while its own contents are still fading */
  #mnav .stag{transition-delay:0s}

  html[data-menu="1"] #burger span:nth-child(1){transform:translateY(7px) rotate(45deg)}
  html[data-menu="1"] #burger span:nth-child(2){opacity:0}
  html[data-menu="1"] #burger span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
  html[data-menu="1"]{overflow:hidden}
  @media (prefers-reduced-motion:reduce){
    #mnav,#mnav .stag,#mnav .mlink{transition:none}
    #mnav .stag{opacity:1;transform:none}
  }
  @media (min-width:1081px){#mnav,#mscrim{display:none}}

  /* ── hero ───────────────────────────────────────────────────────────
     A real photograph pushing in, match-cutting to a procedurally drawn
     array on one canvas. Height is UNCHANGED at min(665vh,5900px) — the
     ground journey's stops are solved against measured section positions,
     and changing the hero's length would move every one of them.

     THE LAYER BUDGET, which is why this is a win rather than a risk. The old
     hero carried three full-viewport beat photographs, two photographic
     overlays, a shutter, a closer, a dusk vignette and a CSS grid — six
     permanently promoted layers after it had already been cut down from
     eighteen. This is one canvas, one photo layer that switches itself off
     once spent, and two gradients that are cheap to repaint. Two layers. */
  /* Shortened twice. 665vh/5900px -> 580vh/5200px was the other half of
     "start faster": the timeline decides WHERE in the run each beat sits, this
     decides how much scrolling the run costs.

     580 -> 518vh IS R1's DOING, and specifically the decision to stop typing
     the two supporting lines. Typing was doing double duty — it was an effect,
     but it was also spending scroll: m1's 143 characters took 520px to type
     before the sentence could be read at all. Handing those lines over whole
     turned that typing time into reading time, which measured 1,116px of
     readable window for m1 against the ~860px a line that length actually
     needs. That surplus is real slack, and the honest place to give it back is
     the hero's length rather than a longer stretch of unchanging text.

     Changing this moves every section below the hero, so the ground journey's
     stop table has to be re-solved against it — see lc-ground.js, and re-run
     review/ranges.mjs rather than eyeballing the section centres. */
  #wrap{position:relative;height:min(518vh,4650px);background:transparent}
  #stage{position:sticky;top:0;height:100vh;overflow:hidden;
    background:linear-gradient(to bottom,#020A1B 0%,var(--stage) 42%,var(--prussian) 100%)}
  /* the stage's own transform is the one thing still worth promoting: it
     drifts and fades during the handover to #after, every frame */
  #stage{will-change:transform}

  /* The photograph. transform-origin is centred because the push is toward
     the middle of the frame — that is where the match-cut lands, so it is
     also what decides which part of the image has to be a panel. */
  #hphoto{position:absolute;inset:0;background-size:cover;background-position:center;
    transform-origin:50% 50%;will-change:transform,opacity;backface-visibility:hidden}
  /* ── R2 HAS NO PHOTOGRAPH ────────────────────────────────────────
     Its opening beat is a strand of current, not a module. This rule is what
     actually prevents the flash on load: #hphoto above sets no opacity, so it
     paints at full strength as soon as it is parsed and lc-hero.js cannot get
     there first — its first frame runs after the first paint. The attribute
     comes from PHP (louth_callan_hero_r2), so the browser knows before it
     reaches the element. display:none rather than opacity:0 because a
     full-bleed layer at zero opacity still holds a compositor layer, and
     #hsnow rides inside it and goes with it. */
  #wrap[data-r2] #hphoto{display:none}
  /* The moving opening, layered over the still inside the SAME element — so
     the ease-back and the fade-out are the parent's and never have to be
     animated twice. It starts transparent and lc-hero.js fades it up on
     `canplay`, so a clip that has not buffered shows the still rather than a
     black hole. object-fit:cover matches the still's background-size:cover, so
     the two crop identically at every aspect ratio and the panel does not
     shift between them. */
  #hvid{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
    object-position:center;opacity:0;transition:opacity var(--mo-move) ease;pointer-events:none}
  #hvid.on{opacity:1}
  /* Same box as the clip, above it. Its alpha is painted rather than styled,
     so it needs no fade class — lc-hero.js multiplies every particle by
     P.photoFade and the field dissolves exactly with the photograph. */
  #hsnow{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
  @media (prefers-reduced-motion:reduce){#hsnow{display:none}}
  /* R2's energy current. NO z-index ON PURPOSE. Every layer in #stage is
     positioned with auto z-index, so they paint in document order — and the
     element is authored between #hcv and #hveil precisely so it lands above
     the array and below the veil and the statements. A z-index here would
     lift it over #hveil and put the strand on top of the copy. */
  #hwave{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
  @media (prefers-reduced-motion:reduce){#hwave{display:none}}
  /* Reduced motion never gets the clip at all — lc-hero.js also declines to
     load it, so this is belt and braces for the frame before scripting runs. */
  @media (prefers-reduced-motion:reduce){#hvid{display:none}}
  #hcv{position:absolute;inset:0;width:100%;height:100%;display:block}

  /* One veil doing the work the scrim, dusk and closer used to do between
     them. A radial darkening off the edges and a lift from the bottom, so
     type reads at every beat without a per-beat overlay. */
  #hveil{position:absolute;inset:0;pointer-events:none;
    background:
      radial-gradient(128% 96% at 50% 44%,transparent 34%,rgba(3,15,38,.74) 100%),
      linear-gradient(to top,rgba(3,15,38,.90) 0%,rgba(3,15,38,.16) 46%,rgba(3,15,38,.40) 100%)}
  .topfade{position:absolute;top:0;left:0;right:0;height:150px;pointer-events:none;
    background:linear-gradient(to bottom,rgba(3,15,38,.75),rgba(3,15,38,0))}

  /* ── the daylight hero (?hero=day) ────────────────────────────────
     PROVISIONAL. These four rules are the whole treatment, and they are the
     plumbing rather than the design — they cannot be tuned properly until the
     daylight photograph exists, because what a veil has to do depends entirely
     on how bright the frame under it is. Nothing here touches the default
     hero: every selector is gated on [data-hero=day], which only ever appears
     when the query parameter is present.

     THE STRUCTURE, WHICH IS THE PART THAT IS DECIDED. It is not a light hero.
     It opens light and darkens into the same field the night version uses by
     the time the x-ray beat arrives. Three reasons: the cyan wireframe and the
     lime charge do not read on white and would otherwise need a second set of
     inks tuned for them; it lands dark at the handover to #after, so that
     transition and the whole ground journey stay exactly as they are; and
     dawn-on-site into the engineering view is a better story than a flat
     lighting choice. Only the first two beats actually differ.

     The darkening is carried by #stage's gradient stops rather than by an
     extra layer, so the layer budget is untouched. */
  #wrap[data-hero=day] #stage{
    background:linear-gradient(to bottom,#C9D4E4 0%,#93A6C0 34%,#1A3050 78%,var(--prussian) 100%)}
  #wrap[data-hero=day] #hveil{
    background:
      radial-gradient(128% 96% at 50% 44%,transparent 44%,rgba(3,15,38,.30) 100%),
      linear-gradient(to top,rgba(3,15,38,.62) 0%,rgba(3,15,38,.04) 40%,rgba(3,15,38,.10) 100%)}
  #wrap[data-hero=day] .topfade{
    background:linear-gradient(to bottom,rgba(3,15,38,.34),rgba(3,15,38,0))}
  /* The statements keep Porcelain: they are typed over the photograph, which
     is the darkest thing in frame at every beat that carries type, and the
     veil above lifts the bottom of the frame specifically so they hold. If the
     daylight photograph turns out brighter than that assumption, this is the
     line that changes, and the ground journey's L=0.2205 snap is the rule to
     follow rather than a hand-picked grey. */

  .stmt{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
    padding:0 clamp(24px,6vw,96px);opacity:0;pointer-events:none;color:var(--porcelain)}
  .stmt h1,.stmt h2{margin:0;text-align:center;font-weight:700;color:var(--porcelain);
    font-size:var(--t-stmt);letter-spacing:-.028em;line-height:1.06;max-width:20ch}
  .lead h1{font-size:var(--t-stmt);letter-spacing:-.03em;line-height:1.02;max-width:17ch}

  /* ── ALL THREE BEATS ARE ONE SIZE ─────────────────────────────────
     Identical to .lead h1 above — same clamp, same weight, same measure, same
     leading. The hero speaks in one voice rather than a headline followed by
     two captions.

     An earlier pass made these subheads at 36px on a 30ch measure, on the
     reasoning that R1's supporting lines are sentences rather than slogans
     and that the old 74px/20ch setting put the second beat at eight lines,
     taller than the viewport it has to be centred in. The first half of that
     was a judgement I was not asked to make; the second half was true of the
     OLD measure and is not true of this one. Measured at the headline's own
     17ch: six lines and 511px at 1440x810, five and 426px for the third —
     both comfortably inside the frame with the nav clear. The wider 20ch
     measure was what pushed it past the fold, not the size.

     STEPPED DOWN ONE NOTCH from 84px to 73px on a note. The pair
     moves together and must keep moving together — the whole point of the
     rule above is that the three beats are one size, so editing one clamp
     without the other silently reintroduces the headline-plus-captions
     hierarchy that was just removed. Same for the mobile override at the
     bottom of this file, which targets both selectors for the same reason. */
  .stmt.sub h2{font-size:var(--t-stmt);font-weight:700;
    letter-spacing:-.03em;line-height:1.02;max-width:17ch}
  .low{padding-top:22vh}

  #cue{position:absolute;left:50%;bottom:34px;transform:translateX(-50%);pointer-events:none;
    display:flex;flex-direction:column;align-items:center;gap:10px;opacity:0;transition:opacity var(--mo-move) ease}
  #cue span{font-size:10.5px;font-weight:700;letter-spacing:.22em;color:rgba(247,247,243,.62)}
  #cue i{display:block;width:1px;height:42px;background:linear-gradient(to bottom,rgba(247,247,243,.6),transparent)}

  /* Under reduced motion the canvas renders its settled operational state
     once and the photograph is never shown pushing — lc-hero.js pins the
     parameters rather than animating them. */
  @media (prefers-reduced-motion:reduce){
    #hphoto{transition:none}
  }
  /* The section below rides up over the pinned hero rather than following it.
     -42vh of overlap starts the lift just as the close settles, so the two
     hand off instead of queueing. */
  /* The card is now a boundary, not a colour — the ground shows through it, so
     it can never disagree with whatever colour the page happens to be. */
  /* No edge line and only a whisper of shadow. With the hero dissolving, the
     fade is the transition — a hard lip on a transparent card just read as a
     stray rule crossing the page. */
  /* More overlap so the card is already climbing during the hero's close, and
     no top padding at all — 26vh of it was pushing the first line of copy to
     981px on a 900px viewport, which is the empty screen. */
  /* The panel. It is painted in var(--now) — the exact colour the ground is at
     that scroll position — so it is indistinguishable from the ground and does
     not punch a hole in the journey, while still being opaque enough to hide
     the fixed footer sitting behind it. That opacity is what makes the reveal
     possible at all: a transparent page would show the footer through itself at
     every scroll position. The rounded bottom corners are the lip that lifts. */
  #after{position:relative;z-index:5;margin-top:-56vh;padding:0;
    background:var(--now);
    /* The two pairs of corners do different jobs and are sized separately.
       The TOP pair ride up over the hero, so the section arrives as a card
       rather than as a hard edge sweeping across a photograph — modest, because
       a large radius there would cut two big notches of photograph out of the
       seam. The BOTTOM pair are the lip that lifts off the footer, and that is
       the moment the whole reveal is built around, so they are much larger. */
    border-top-left-radius:clamp(20px,3vw,44px);
    border-top-right-radius:clamp(20px,3vw,44px);
    border-bottom-left-radius:clamp(28px,4vw,84px);
    border-bottom-right-radius:clamp(28px,4vw,84px);
    /* A HAIRLINE ON THE LIP, because the reveal cannot rely on colour alone
       any more. It used to: every page ended on Porcelain and lifted off a
       Prussian footer, which is a 17:1 edge and needs no help. Interior pages
       now end on Imperial — one crossing instead of three — and Imperial
       against Prussian is 1.36:1. The lip was still there, still rounded,
       still moving, and effectively invisible.
       Two dark colours cannot produce an edge between them, so the edge comes
       from a light line instead. It is self-adjusting rather than conditional:
       on a Porcelain card it is light-on-light and simply does not show, which
       is correct, because there the 17:1 colour difference is already doing
       the work. Offset down and unblurred, so it traces the bottom curve only
       and never touches the top corners riding over the hero. */
    box-shadow:0 1px 0 rgba(247,247,243,.16)}
  html[data-footreveal="1"] #after{margin-bottom:var(--foot-h,0px)}
  /* The hero has to out-rank the footer, or the fixed panel paints over the
     bottom of the screen while you are still at the top of the page. */
  #wrap{z-index:2}
  /* A full screen minimum meant every short section carried half a screen of
     empty above and below it. The chamber sections set their own heights and
     are untouched by this. */
  main section{min-height:78vh;display:flex;align-items:center;
    padding:0 clamp(24px,6vw,96px);position:relative}
  /* The first one leads with its copy rather than centring it, so the content
     rides up with the card instead of trailing half a screen behind it. */
  /* The deck sets this section's height, so it needs its own bottom room —
     without it the last card was still stuck while the next section had already
     scrolled into view, which is the overlap. */
  main section:first-child{align-items:flex-start;padding-top:13vh;padding-bottom:8vh;min-height:0}
  /* ── The Record: a deck ───────────────────────────────────────────
     Each figure is sticky at a slightly lower offset than the one before, so
     the covered ones leave a lip showing — the manila-folder read. Their own
     bottom margin is what supplies the scroll length for the collapse. */
  main #record .split{align-items:start}
  /* The copy no longer sticks. It was holding for the whole deck, which is why
     "New Company. Not a New Team." was still pinned at the top of the screen
     while Who We Are had already scrolled in — two sections at once. */
  /* The hold. A sticky card only stays pinned while its containing block is
     still passing — and that block is .deck, whose bottom is the last card's
     bottom. So the last card unpinned the instant it arrived and the finished
     three-high stack existed for about zero pixels of scroll. The room has to
     be INSIDE .deck to count; the section's own bottom padding sits outside it
     and buys nothing. */
  main .deck{position:relative;padding-bottom:30vh}
  /* Fixed height, not content height. A reel digit is an inline-block sitting on
     the baseline, so it pushes the line box to 1em + the font's descender — 89px
     rather than 68px — and the '$' span changes which inline is tallest. That
     made every card a different size, which is the scattered stack. Fixing the
     card height sidesteps the whole line-box question. */
  /* All three stick on the same line, level with the heading. The per-card
     offset I had before made the stack drift downward, which is what stopped it
     reading as a collapse. Depth comes from the covered cards rising and
     shrinking behind the active one instead. */
  /* Each card pins at its own offset, 22px lower than the one before it. That
     single line is the whole deck. With all three sharing one top the arriving
     card landed on exactly the same line as the one already pinned and covered
     it completely — the earlier card was still there, still stuck, but with
     nothing showing, which reads as "it left". Staggering the rest positions
     leaves a band of each earlier card visible above the newer one, so the pile
     builds as you scroll and holds when it's done.
     This is the per-card sticky offset that was previously rejected for
     drifting the deck downward. At 22px on a 184px card it's a lip, not a
     drift — the top line stays at 150px and only the front card's bottom moves. */
  /* ── The Record: an opposite-parallax stack ───────────────────────
     Not sticky cards. Sticky was the whole problem: a sticky card has to
     scroll up to its pin point before it pins, so the first card was always
     travelling before it settled, and once pinned it appears to slide DOWN
     relative to the copy scrolling past it. Both readings are "the card moves".
     Instead the whole composition is pinned across 240vh, and the cards are
     absolutely positioned inside it. The first card is placed once at rest and
     is never given a transform again — it cannot move, because nothing is
     animating it. The later cards ride up from below the fold and land 22px
     lower each, which is the manila tab.

     THE BAR TREATMENT WAS HERE FOR AN AFTERNOON AND IS NOT ANY MORE. The
     animatedstatspro reference was applied to this section on 12 Aug and moved
     to the About page's stats block the same day — that was the one meant.
     The bar's own rules live further down under .rec-bar, where the stats
     block uses them; nothing here depends on them. */
  main #record{display:block;height:240vh;min-height:0;padding-top:0;padding-bottom:0}
  /* Centred in the pin, but pulled up: the composition is a good deal shorter
     than the screen, so a true centre left a large empty band above it — which
     is the first thing you see as the panel rides in, and reads as the section
     being late rather than as breathing room. */
  main #record .split{position:sticky;top:0;height:100svh;align-items:center;
    padding-bottom:13vh;grid-template-columns:minmax(0,5fr) minmax(0,7fr)}
  main #record .copy{position:static}
  /* 184 card + one 22px tab per card BEYOND the first. R1 added a fourth
     figure (28 States), so this stepped 228 -> 250 and the mobile height
     196 -> 214. The deck's JS solves its own timing for any card count, but
     this height is the one thing it cannot infer — leave it short and the
     last card is clipped by the section's own bottom edge. */
  main #record .deck{position:relative;height:250px;padding-bottom:0}
  main #record .fig{position:absolute;left:0;right:0;top:0;margin:0;
    will-change:transform}
  /* Mobile now runs the same deck mechanism as desktop — absolute cards in a
     fixed-height stage, ridden by transforms — just sized for the narrow
     column. The generic .split collapse stacks copy above the stage. */
  @media (max-width:900px){
    main #record{height:200vh}
    /* This section never actually collapsed. The generic stack rule is
       `main .split{grid-template-columns:1fr}` — one class weaker than
       `main #record .split`, which sets the 5fr/7fr tracks unconditionally,
       so the tracks survived the breakpoint and copy and deck stayed side by
       side in two columns far too narrow for either. Reset them here, at the
       specificity that set them. */
    main #record .split{grid-template-columns:1fr;align-items:start;gap:26px;
      padding:calc(var(--head-h) + 14px) 0 0}
    main #record .copy{max-width:none}
    main #record .lede{max-width:40ch}
    /* 150px card plus two lips — 22px between 760 and 900, 16px below that,
       so this covers the taller of the two rather than either exactly. */
    main #record .deck{height:214px}
    main #record .fig{padding:20px 24px 18px}
  }

  /* THE BAR. Navy rather than the reference's near-black: the reference is a
     showcase on white and this sits on Porcelain in a palette that already has
     a dark — using #0A0A0A here would introduce a second one that agrees with
     nothing else on the page. Same structure, brand ink. */
  main .rec-bar{position:relative;margin-top:clamp(34px,4.5vh,54px);
    /* Cell count comes from the block, not from a hard-coded 4 — the homepage
       Record carried four figures and About carries three, and a bar that
       assumes four leaves an empty track on any other page that adopts it. */
    display:grid;grid-template-columns:repeat(var(--cells,4),minmax(0,1fr));
    /* ── VERTICAL, NOT RAKED, AND THAT IS THE WHOLE POINT ─────────────
       This was a 100deg gradient running Prussian to a lighter Imperial ACROSS
       the bar. It gave the panel direction, and it also gave every cell a
       different colour from its neighbour — so each one looked like its own
       tile and the row read as four joined cards rather than as one bar. That
       is the "should be a bar not separate cards" note; the dividers were only
       half of it.
       Turned vertical, every cell shares an identical fill at every x, so
       nothing distinguishes one from the next except the hairline. The panel
       is one continuous surface that happens to be divided — which is exactly
       what the reference does with its flat near-black.
       Kept as a gradient rather than a flat colour because the bar is 176px of
       unbroken navy and a top-lit surface sits under the current better than a
       slab does; it is a 3% move, invisible as a step and present as depth. */
    background:linear-gradient(180deg,#0A2C71 0%,var(--imperial) 46%,#061F55 100%);
    border-radius:var(--r-lg);overflow:hidden;
    box-shadow:0 26px 60px rgba(3,15,38,.22)}
  /* The current. Behind the figures, above the gradient, and never clickable. */
  main .rec-flow{position:absolute;inset:0;width:100%;height:100%;
    pointer-events:none;z-index:0}
  main .rec-cell,main .rec-cell + .rec-cell::before{z-index:1}
  /* minmax(0,1fr) AND NOT 1fr. "Nationwide" is a word rendered at the numeral
     size — by far the widest cell — and a bare 1fr is min-content-floored, so
     that one cell would steal width from the other three and the dividers
     would sit off the quarters. The 0 minimum lets all four stay equal and
     lets the long one shrink its own type instead (see .rec-num below). */
  main .rec-cell{position:relative;padding:clamp(24px,3vw,38px) clamp(18px,2.4vw,32px);
    display:flex;flex-direction:column;justify-content:center;align-items:center;
    text-align:center;min-height:clamp(128px,15vh,168px)}
  /* Hairlines BETWEEN cells only, drawn as a left edge on every cell after the
     first — a border on all four would draw on the outside of the panel too
     and the rounded corners would clip it into stubs. */
  /* EDGE TO EDGE, and this is the change that stopped four cells reading as
     four cards. Inset hairlines (they were top:18%/bottom:18%) draw a fence
     around each figure with clear space above and below it, which is exactly
     the silhouette of a card in a row of cards. Run to the full height and the
     same line becomes a division OF one bar rather than a gap BETWEEN four. */
  main .rec-cell + .rec-cell::before{content:'';position:absolute;
    left:0;top:0;bottom:0;width:1px;background:rgba(247,247,243,.14)}
  /* The reel is an inline-block on the baseline, so the line box is taller
     than the glyphs and the '$' changes which inline is tallest — the deck
     answered that with a fixed card height, and this answers it with a fixed
     row instead, so all four numerals sit on one line whatever they contain. */
  main .rec-num{margin:0;display:flex;align-items:baseline;justify-content:center;
    min-height:1.06em;
    font-size:clamp(30px,3.6vw,52px);font-weight:700;letter-spacing:-.035em;
    line-height:1.06;color:var(--porcelain);white-space:nowrap}
  /* See the note at the markup: a word figure at the numeral size fills its
     cell. Sized to sit as a peer of the numbers rather than as a headline. */
  main .rec-num--word{font-size:clamp(22px,2.5vw,34px);letter-spacing:-.02em}
  main .rec-unit{font-size:.52em;font-weight:700;margin-left:.14em;
    letter-spacing:-.01em;color:var(--pacific)}
  main .rec-cur{font-size:.66em;font-weight:700;margin-right:.02em}
  main .rec-cap{margin:12px 0 0;max-width:22ch;
    font-size:11.5px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
    line-height:1.45;color:rgba(247,247,243,.62)}
  /* 22ch is the measure for a quarter-width cell. A bar with ONE figure is the
     full panel, and the same cap turns its caption into a narrow three-line
     column under a very wide number — see Solar's Constructed Nationwide.
     :only-child would not match — the .rec-flow canvas is a sibling inside the
     bar, so the sole cell is the only DIV rather than the only child. */
  main .rec-bar > .rec-cell:first-of-type:last-of-type .rec-cap{max-width:48ch}

  /* ONE COLUMN BELOW 760, and the hairline turns with it. There is no 2x2
     step any more: the grid is now count-driven, and a 2x2 rule can only be
     written for one specific count — at three figures it leaves an orphan
     cell whose dividers point the wrong way. Straight to a stack is correct
     for any number, and the bar simply becomes a tall panel. */
  @media (max-width:760px){
    main .rec-bar{grid-template-columns:1fr}
    main .rec-cell + .rec-cell::before{left:0;right:0;top:0;bottom:auto;
      width:auto;height:1px}
  }
  main .fig{position:sticky;top:150px;
    z-index:calc(var(--i,0) + 1);
    height:184px;display:flex;flex-direction:column;
    background:var(--panel);border:1px solid var(--edge);border-radius:var(--r-lg);
    padding:26px 32px 24px;margin-bottom:26vh;
    box-shadow:0 22px 50px rgba(3,15,38,.16);
    transition:background var(--mo-ui) ease,border-color var(--mo-ui) ease}
  main .fig:last-child{margin-bottom:0}
  main .num{flex:none}
  main .cap{margin-top:auto}
  @media (max-width:900px){
    main #record .copy{position:static}
    main .fig{top:calc(96px + var(--i,0) * 16px);height:150px;margin-bottom:20vh;padding:20px 22px 18px}
  }
  main section > div{max-width:1100px;margin:0 auto;width:100%}
  main h3{margin:0 0 16px;font-size:var(--t-h3);font-weight:700;
    letter-spacing:-.025em;line-height:1.08;color:var(--fg);transition:color var(--mo-ui) ease}
  main p{margin:0;max-width:54ch;font-size:16.5px;line-height:var(--lh-body);color:var(--fg-dim);
    transition:color var(--mo-ui) ease}
  main .split{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:clamp(32px,5vw,80px);
    align-items:start;max-width:1180px;margin:0 auto;width:100%}
  main .lede{margin:22px 0 0;max-width:44ch}
  main .figs{display:grid;grid-template-columns:1fr;gap:0}
  /* The reel centres each digit in a .62em column, so its ink sits 3.5px inside
     the box at 68px while the caption's sits 1.0px in — measured off the font.
     Pulling the number out by the difference puts the two on the same edge. */
  /* -.03em was tight enough that the unit crowded the last digit. Letter
     spacing is applied after each glyph, so easing it does not move the left
     edge and the optical pull above stays correct. */
  main .num{margin:0 0 0 -.037em;font-size:clamp(42px,4.6vw,68px);font-weight:700;
    letter-spacing:-.016em;line-height:1;color:var(--fg);max-width:none;
    transition:color var(--mo-ui) ease}
  main .unit{font-size:.4em;font-weight:700;margin-left:8px;color:var(--fg-accent);
    transition:color var(--mo-ui) ease}
  main .cur{font-size:.66em;vertical-align:baseline;margin-right:1px}
  main .cap{margin:10px 0 0;font-size:14px;line-height:1.45;color:var(--fg-dim)}
  /* The figures qualifier (R1). Deliberately quieter than the lede and clearly
     subordinate to it — it is a qualification of the numbers, not a third
     paragraph of pitch — but NOT hidden: it exists because the figures are the
     leadership team's combined record rather than the company's alone, and a
     disclaimer nobody can read does not do that job. Held above 4.5:1 by
     using --fg-dim at a small size rather than dropping opacity further. */
  main .disclaimer{margin:20px 0 0;max-width:52ch;font-size:12.5px;line-height:1.5;
    color:var(--fg-dim);opacity:.82;transition:color var(--mo-ui) ease}
  @media (max-width:900px){ main .split{grid-template-columns:1fr;gap:44px} }
  /* ── Who We Are: centred and minimal ─────────────────────────────
     One idea per screen, everything on the centre line. No media column, no
     capability rows — the section carries a single claim and a way in. */
  main .mid{max-width:940px;margin:0 auto;width:100%;text-align:center}
  /* R1's Who We Are lede is ~370 characters where the old one was 100. The
     generic .lede measure is 44ch, which is right for a short centred line and
     wrong for a paragraph — at 44ch this one runs to eight centred lines,
     which is a wall rather than a lede. Widened and given real leading. It
     stays centred because the whole section is a centred composition; only the
     measure changes. */
  main #about .lede{max-width:68ch;line-height:1.62;font-size:17px}
  /* The eyebrow is a <p>, so it was picking up main p's 54ch cap. A 54ch block
     with margin:0 sits hard left, and text-align:center then centres the words
     inside THAT box rather than in the section — which is why "Who we are" sat
     out on its own while the heading, lede and pill were all on the centre
     line. The lede escaped it because it already had margin:auto. */
  main .mid .eyebrow{max-width:none;margin-left:auto;margin-right:auto}
  main .mid h3{font-size:clamp(38px,6.2vw,88px);letter-spacing:-.035em;line-height:1.02;
    margin:0 0 4px}
  main .mid .lede{margin:30px auto 0;max-width:48ch;font-size:17.5px}
  main .pill{display:inline-flex;align-items:center;gap:10px;margin-top:38px;
    padding:15px 30px;border-radius:var(--r-pill);text-decoration:none;font-size:var(--t-btn);font-weight:700;
    background:var(--fg);color:var(--now);
    transition:background var(--mo-ui) ease,color var(--mo-ui) ease,transform var(--mo-ui) cubic-bezier(.22,1,.36,1)}
  main .pill:hover{transform:translateY(-2px)}
  main .pill::after{content:'\2192'}
  /* The brand teal, with navy type on it rather than white: #030F26 on #00A1AF
     is 7.4:1, white is 2.6:1 and would fail AA at this size. Both sides are
     literal so the button holds its colour through the ground journey. */
  main .pill--teal{background:var(--pacific);color:var(--prussian)}
  main .pill--teal:hover{background:var(--mint);color:var(--prussian)}
  /* ── UNUSED ON THE HOMEPAGE ───────────────────────────────────────
     .caps, .cap-row, .frame and .figs are styled but matched by nothing in
     the current markup — leftovers from the capability rows that were cut
     from Who We Are and the media column that was cut from Leadership.
     They are retained rather than deleted because the interior pages (item F:
     About Louth, Leadership, What We Do) are the obvious home for a
     label/description row and a portrait frame, and re-deriving them costs
     more than carrying them. Delete on sight if those pages land without
     them. Everything else in this file is live. */
  main .caps{margin-top:46px}
  main .cap-row{display:grid;grid-template-columns:minmax(150px,210px) 1fr;gap:22px;
    border-top:1px solid var(--edge);padding:22px 0;transition:border-color var(--mo-ui) ease}
  main .cap-row:last-child{border-bottom:1px solid var(--edge)}
  main .cap-row h4{margin:0;font-size:18px;font-weight:700;letter-spacing:-.012em;
    color:var(--fg-accent);transition:color var(--mo-ui) ease}
  main .cap-row p{margin:0;font-size:15px;line-height:1.55;max-width:46ch}
  main .media{position:sticky;top:110px}
  main .frame{aspect-ratio:3/4;border-radius:var(--r-md);background-size:cover;background-position:center;
    box-shadow:0 26px 60px rgba(3,15,38,.28)}
  /* ── Leadership ───────────────────────────────────────────────────
     The cap-row stack was written to sit under a copy block in one column.
     In a split it becomes the right-hand column, so the 46px it used to need
     for separation now only knocks it out of line with the heading. 7px is
     the cap-height offset that puts the first rule level with the h3. */
  /* ── How We Deliver: static copy, scrubbed film ───────────────────
     The 250vh chamber that ran a four-phase staged reveal is still gone, and
     so is all of its text choreography — the word-by-word inking, the 3D roll
     cascades, the second paragraph. That was what the "remove the scroll
     effect" note was about.

     The FILM keeps its scroll effect. The section is a chamber again, but a
     shorter one, and it holds only the video: the copy sits above it in normal
     flow and never moves. So the two halves of the old section were separated
     rather than one being deleted — the text got calm, the film stayed alive.

     One deliberate departure from the original scrub. It used to shrink the
     card to .58 AND fade it to zero, because the copy was crossfading up into
     the screen the card gave up. The copy is permanent now, so a vanishing
     card would just be the video deleting itself halfway down the section.
     The card zooms and holds instead; nothing fades out. */
  main #approach{display:block;position:relative;padding:clamp(84px,10vh,138px) 0 0}
  main #approach .app-inner{max-width:1180px;margin:0 auto;width:100%;
    padding:0 clamp(24px,6vw,96px)}
  main #approach .app-copy{max-width:940px;margin:0 auto;text-align:center}
  main #approach .app-copy .eyebrow{max-width:none;margin-left:auto;margin-right:auto}
  /* the heading matches Who We Are exactly — same clamp, tracking and leading —
     so the two centred statements read as siblings */
  main #approach .app-copy h3{margin:0 auto 4px;font-size:clamp(38px,6.2vw,88px);
    letter-spacing:-.035em;line-height:1.02}
  main #approach .app-copy .lede{margin:26px auto 0;max-width:56ch;font-size:17.5px}

  /* The chamber. Its only job is to buy scroll distance for the zoom — the
     card is the sole thing pinned inside it, so there is no fitting problem
     between a big centred heading and a 16/9 film in one viewport, which is
     what forced the original to crossfade them in the first place. */
  /* 190vh gave the zoom 810px of travel at a 900px viewport and left an 897px
     tail of empty chamber between the film and the section after it. The scrub
     is a .82-to-1.03 scale; 450px of travel carries it perfectly well, and the
     tail halves. */
  main #approach .app-scroll{position:relative;height:150vh;
    margin-top:clamp(38px,5vh,64px);
    /* ESCAPES THE 1100px MEASURE, same trap as .lc-phero-stage. `main section >
       div` caps direct div children at 1100px, and .app-inner above overrides
       it to 1180 while this chamber never did — so the film was rendering
       NARROWER than the paragraph introducing it. At 1440 that cost the card
       253px of width and, because it is centred in a viewport-tall pin, the
       height it gave up came back as empty band above and below it: 113px a
       side rather than 41. The card's own `min(100%,1180px)` was already the
       intended measure and could not reach it. */
    max-width:none}
  main #approach .app-pin{position:sticky;top:0;height:100svh;
    display:flex;align-items:center;justify-content:center;
    padding:calc(var(--head-h) + 26px) clamp(24px,6vw,96px) clamp(28px,5vh,56px)}
  main #approach .app-card{position:relative;width:min(100%,1180px);
    aspect-ratio:16/9;max-height:100%;border-radius:var(--r-lg);overflow:hidden;
    background:var(--panel) center/cover no-repeat;border:1px solid var(--edge);
    box-shadow:0 22px 50px rgba(3,15,38,.14);
    /* Promoted because it genuinely moves every frame while the chamber is
       passing, and only then — lc-sections.js clears will-change once the
       scrub is done, so this does not hold a full-width layer for the rest of
       the document. Same discipline as the scope rail. */
    transform-origin:50% 50%;backface-visibility:hidden;
    transition:background-color var(--mo-ui) ease,border-color var(--mo-ui) ease}
  main #approach .app-scroll[data-live="1"] .app-card{will-change:transform}
  main #approach .app-card video{position:absolute;inset:0;width:100%;height:100%;
    object-fit:cover;display:block;
    /* counter-scale for depth: the frame and its contents move at different
       rates, so the card reads as a window onto the film rather than as a
       picture being resized */
    transform-origin:50% 50%}
  @media (prefers-reduced-motion:reduce){
    main #approach .app-scroll{height:auto}
    main #approach .app-pin{position:static;height:auto;padding-left:0;padding-right:0}
  }
  @media (max-width:900px){
    main #approach{padding:clamp(66px,8vh,104px) 0 0}
    main #approach .app-inner{padding:0 clamp(18px,5vw,40px)}
    /* NO PIN AND NO CHAMBER ON A PHONE. The scrub bought its zoom with a
       160vh chamber holding one 4/3 card centred in a viewport-tall pin: the
       card filled 290px of a 611px box, so 161px of empty ground sat above it
       and 161px below, and that half-empty view was held for 1182px of
       scrolling. The zoom itself is .82 to 1.03 on a 387px card, which is
       nearly invisible at that size — it was not worth a screen and a half of
       scrolling past dead space to deliver.
       In flow the card keeps its full width and the gaps become ordinary
       section spacing. This is the arrangement prefers-reduced-motion has
       always been given; the phone now gets it too. */
    main #approach .app-scroll{height:auto;margin-top:clamp(26px,3.5vh,38px)}
    main #approach .app-pin{position:static;height:auto;
      padding:0 clamp(18px,5vw,40px)}
    main #approach .app-card{aspect-ratio:4/3}
  }
  /* ── Leadership: calm split ─────────────────────────────────
     Was a 260vh scroll chamber: a dither field rose through it, the headline
     converged out of scatter, and the frame arrived as seven sheared slices
     that sealed under a green seam. Read as noise — removed in full. What is
     left is an ordinary two-column section at ordinary height.
     The media slot is a neutral placeholder until the crew photography
     arrives; replace the .ph block with the image and nothing else changes. */
  /* The bottom padding is deeper than the top on purpose: it is the band the
     dither field's transmission line runs through. At equal padding the line
     had nowhere to go but across the middle of the section, where the opaque
     media panel swallowed the travelling pulse for a third of its run — and
     that panel becomes a photograph, so the problem was permanent. The line
     now runs clear of both columns and still stops well short of #after's
     bottom corner radius. */
  main #leadership{display:block;position:relative;
    padding:clamp(84px,10vh,138px) 0 clamp(150px,17vh,220px)}
  /* The field is back, but as atmosphere only — it sits behind the split and
     is driven by the section crossing the viewport, not by a 260vh pin. The
     chamber, the converging headline and the seven sheared slices stay gone. */
  #lcv{position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none}
  main #leadership .lead-pin{position:relative;z-index:1;padding:0 clamp(24px,6vw,96px)}
  /* Entrance is a plain fade-and-rise on a one-way latch. Hidden only when
     the script is running and motion is welcome — without .lcjs on <html>
     everything renders visible, so no-JS and reduced-motion never get a
     blank section. */
  main #leadership .rise{transition:opacity var(--mo-slow) cubic-bezier(.22,1,.36,1),transform var(--mo-slow) cubic-bezier(.22,1,.36,1)}
  html.lcjs main #leadership .rise{opacity:0;transform:translateY(24px)}
  html.lcjs main #leadership .rise.in{opacity:1;transform:none}
  html.lcjs main #leadership .copy > .rise:nth-child(2){transition-delay:.07s}
  html.lcjs main #leadership .copy > .rise:nth-child(3){transition-delay:.14s}
  html.lcjs main #leadership .copy > .rise:nth-child(4){transition-delay:.21s}
  html.lcjs main #leadership .media .rise{transition-delay:.12s}
  /* ── THE PHOTOGRAPH RUNS OFF THE RIGHT EDGE ───────────────────────
     It was a floating rounded card with even margin on both sides, which is
     the safest and least interesting thing a photograph can do — it read as an
     illustration dropped beside the copy rather than as the section's other
     half.

     THERE ARE TWO THINGS HOLDING IT IN, not one, and only finding the first
     is why the initial attempt did nothing:
       1. .lead-pin's own gutter, clamp(24px,6vw,96px);
       2. `main section > div{max-width:1100px;margin:0 auto}` up at the top of
          this file, which centres EVERY direct div child of a section. At
          1440 that is another 170px a side, and it is invisible until you
          measure the pin and find it is 1100 wide inside a 1440 window. The
          same rule has caught the rail and the masthead — see the notes at
          both — so it is now caught three times and worth reading for.

     SO THE PIN STOPS CONSTRAINING AND THE GRID CARRIES THE INSET INSTEAD.
     The pin goes full width with no padding, and .split takes the centring and
     the gutter as a single padding-LEFT. The right side then has nothing
     holding it and the image ends flush with the window.

     NO vw ANYWHERE IN THAT SUM. 100vw includes the classic scrollbar on
     Windows and Linux, so a vw-based bleed overhangs by ~15px there and puts a
     horizontal scrollbar on the page — which then makes 100vw bigger again.
     The percentage below resolves against the pin, which is the section width,
     which is the real client width. It cannot overhang.

     The radius survives on the left corners only. A rounded corner against the
     window edge is the tell that something is a box near the edge rather than
     something running off it; square is what makes it read as continuing. The
     shadow went with it — it was casting onto a gutter that no longer exists.

     The grid tips to the image as well, 5fr/7fr to 4fr/7fr: the copy column
     was sized for a heading that has since become a paragraph. */
  main #leadership .lead-pin{max-width:none;padding:0}
  main #leadership .split{align-items:center;max-width:none;margin:0;
    grid-template-columns:minmax(0,4fr) minmax(0,7fr);
    padding-left:calc(max(0px, (100% - 1100px) / 2) + clamp(24px,6vw,96px))}
  /* R1's heading is a SENTENCE — "The people making decisions are directly
     involved in every project." — where the old one was a three-word title.
     At the display size and the 5fr column it broke to five cramped lines and
     ran up under the fixed cap. A sentence wants to be read, so it steps down
     a size and gets real leading; the copy below it is what carries the
     detail, and the two now read as one voice instead of a title shouting
     over a paragraph. */
  main #leadership h3{font-size:clamp(24px,2.6vw,36px);line-height:1.18;
    letter-spacing:-.02em;max-width:17ch}
  main #leadership .media{position:static}
  /* The one opaque rectangle in the section — it stands where a photograph
     will, so it covers the ground exactly as the photograph would. */
  main #leadership .ph{aspect-ratio:4/3;border-radius:var(--r-md);
    background:#E9EBEE;border:1px solid rgba(10,27,63,.10);
    display:flex;align-items:center;justify-content:center}
  /* the same box, carrying a photograph instead of a placeholder glyph.
     Left corners only, no shadow — see the bleed note above.

     3/2, AND THE PHOTOGRAPH DICTATES IT. This was 5/4, chosen when the picture
     here was three people standing together — at that width 5/4 cropped
     nothing and gave the image presence against the copy instead of reading as
     a band across the page. The 11 Aug photograph is thirteen people in ONE
     LINE spanning about 5110px of a 7008x3944 frame, and that changes the
     arithmetic rather than the taste: a 5/4 window wide enough to hold 5110px
     needs 4088px of height and the negative only has 3944. It is not a crop
     that can be chosen better — measured on the render, 5/4 took the left
     man's shoulder and bare foot and the right man's arm.
     3/2 needs 3407px and fits with room to spare.

     BOTH BREAKPOINTS CARRY THE SAME RATIO on purpose (see the narrow rule),
     because the img is object-fit:cover over one file: any box that is not the
     file's own 3/2 goes back to cropping the ends off the group. If this ever
     wants 5/4 presence again it needs a different photograph, not a different
     number here. */
  main #leadership .ph.shot{background:none;border:0;overflow:hidden;
    aspect-ratio:3/2;border-radius:var(--r-md) 0 0 var(--r-md)}
  main #leadership .ph.shot img{display:block;width:100%;height:100%;object-fit:cover}
  main #leadership .ph svg{width:clamp(46px,7vw,72px);height:auto;
    fill:none;stroke:#A6ADB8;stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round}
  main #leadership .ph svg circle{fill:#A6ADB8;stroke:none}
  @media (max-width:900px){
    main #leadership{padding:clamp(66px,8vh,104px) 0 clamp(120px,14vh,170px)}
    /* One column at this width, so there is no second column to bleed and the
       photograph goes back to being inset on both sides. The gutter moves back
       onto the pin, and .split's padding-left — which is doing the desktop
       centring — has to be cleared or it double-counts. */
    main #leadership .lead-pin{padding:0 clamp(18px,5vw,40px)}
    /* grid-template-columns HAS TO BE REPEATED HERE. The stack for every other
       split comes from `main .split{grid-template-columns:1fr}` in this same
       block, and the desktop rule above is `main #leadership .split` — one
       class stronger. A media query adds no specificity, so the stronger
       selector keeps winning inside it and the section stayed two columns all
       the way down to 390px, with the photograph 195px wide. Measured, not
       assumed; it looked fine in the desktop shot. */
    main #leadership .split{padding-left:0;grid-template-columns:1fr}
    main #leadership .media{margin-top:24px}
    /* 3/2 here too — matching the file, not the placeholder. 4/3 was safe for
       the three-person photograph and slices the ends off the thirteen-person
       one, because cover crops the sides of a 3/2 file to fill it. The
       placeholder above keeps its own 4/3; it has nothing to crop. */
    main #leadership .ph.shot{aspect-ratio:3/2;border-radius:var(--r-md)}
  }

  /* ── Footer ───────────────────────────────────────────────────────
     Fixed to the bottom of the viewport and sitting BEHIND the page, which
     scrolls up off it to reveal it. It no longer takes its colour from the
     ground: it is a permanent dark plate, so its own type has to be fixed too
     or it would go invisible whenever the journey happened to be dark.
     If the footer is too tall to fit the viewport it falls back to normal flow
     — a fixed panel taller than the screen would clip its own content. */
  /* The plate bleeds 96px up behind the panel. The corner notches occupy the
     84px above the panel's bottom edge, and with the plate top flush to that
     edge the only thing behind the notches was #ground — painted the same
     Porcelain as the panel, so the corners filled with their own colour and
     read as a straight seam. The bleed is dead padding: in normal flow the
     negative margin cancels it so the layout is unmoved, and in the fixed
     reveal the reveal distance is shortened by the same 96px, so the plate top
     always sits above the corners and the notches always cut into navy. */
  /* The bottom pad was 52px against 52px above the grid, so the plate read as
     bottom-heavy at the copyright line and short beneath it (Katie, 14 Aug
     2026). Scaled with the viewport instead of fixed, so it holds on a phone
     and opens up on a desktop. */
  #site-foot{position:relative;z-index:1;background:var(--prussian);color:var(--porcelain);
    margin-top:-96px;padding:96px clamp(24px,6vw,96px) clamp(64px,7vh,108px)}
  html[data-footreveal="1"] #site-foot{position:fixed;left:0;right:0;bottom:0;margin-top:0}
  /* No rule across the top any more — the footer already announces itself
     by colour and by the lip of #after lifting off it. The padding stays;
     it was spacing content off the divider, and it still spaces it off the
     top edge. (96px of the section's own padding is eaten by the -96px
     margin that makes the reveal work, so this 52px is the real top gap.) */
  /* ── the footer grid ──────────────────────────────────────────────
     A faint field behind the links, to stop the plate reading as a flat
     rectangle. Two things make it belong here rather than being generic
     background texture:

     THE CELLS ARE MODULE-SHAPED. 88 x 50 is 1.76:1, which is the same ratio
     as PW/PL in lc-hero.js — the proportion of the panels the hero draws
     2,700 of. Nobody will name it, but a square graph-paper grid would have
     been decoration and this is the same object at rest.

     IT IS MASKED, NOT TILED TO THE EDGES. Ruled lines running into all four
     corners read as a table. The radial mask keeps it strongest under the
     headings and gone by the outer edges, so it is a field the content sits
     in rather than a box drawn around it.

     Alpha is .075: on #030F26 that is around a three-value step. It reads as
     structure rather than as something to look at, which is the ceiling worth
     respecting here — the footer is the last thing on the page and its job is
     to be scanned, not admired. The mask carries the rest of the lift: the
     solid core reaches further before it starts falling away, so more of the
     field is at full strength rather than the lines simply being darker.
     It is a pseudo-element so there is no extra node, and pointer-events:none
     so it can never take a click off a link. */
  #site-foot::before{content:'';position:absolute;inset:0;z-index:0;pointer-events:none;
    background-image:
      repeating-linear-gradient(to right,rgba(127,208,215,.075) 0 1px,transparent 1px 88px),
      repeating-linear-gradient(to bottom,rgba(127,208,215,.075) 0 1px,transparent 1px 50px);
    -webkit-mask-image:radial-gradient(134% 114% at 50% 4%,#000 22%,transparent 84%);
    mask-image:radial-gradient(134% 114% at 50% 4%,#000 22%,transparent 84%)}
  /* Lifted above the grid. Without this the ::before — positioned, so painted
     in the positioned-descendant pass — would lay its lines over the type
     rather than behind it. */
  #site-foot .wide{position:relative;z-index:1;
    max-width:1180px;margin:0 auto;width:100%;padding-top:52px}
  #site-foot .fgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
    gap:clamp(28px,4vw,64px)}
  #site-foot h4{margin:0 0 16px;font-size:11.5px;font-weight:700;letter-spacing:.14em;
    text-transform:uppercase;color:var(--mint)}
  #site-foot ul{margin:0;padding:0;list-style:none}
  #site-foot li{margin:0 0 10px;font-size:14.5px;line-height:1.55;color:rgba(247,247,243,.6)}
  #site-foot a{color:inherit;text-decoration:none;transition:color var(--mo-ui) ease}
  #site-foot a:hover{color:var(--porcelain)}
  #site-foot .fbase{display:flex;justify-content:space-between;gap:24px;flex-wrap:wrap;
    margin-top:48px;padding-top:22px;border-top:1px solid rgba(247,247,243,.14);
    font-size:13px;color:rgba(247,247,243,.45)}
  @media (max-width:900px){
    #site-foot .fgrid{grid-template-columns:repeat(2,minmax(0,1fr));gap:36px}
  }
  @media (max-width:560px){
    #site-foot .fgrid{grid-template-columns:1fr}
  }
  @media (max-width:900px){
    main .cap-row{grid-template-columns:1fr;gap:8px}
    main .media{position:static;margin-top:36px}
  }

  /* ── What We Do: the scope rail ───────────────────────────────────
     Glass on the caption panel only, never the whole surface. Each card is
     contained so its hover work stays local. */
  main .wide{max-width:1180px;margin:0 auto;width:100%}
  main .rail-head{display:flex;align-items:flex-end;justify-content:space-between;gap:32px;flex-wrap:wrap}
  main .rail-nav{display:flex;gap:10px;flex:none;padding-bottom:4px}
  main .rail-nav button{display:inline-flex;align-items:center;justify-content:center;
    width:44px;height:44px;border-radius:var(--r-pill);cursor:pointer;background:transparent;
    border:1px solid var(--edge);color:var(--fg);
    transition:background var(--mo-ui) ease,border-color var(--mo-ui) ease,color var(--mo-ui) ease,opacity var(--mo-ui) ease}
  main .rail-nav button:hover{background:var(--fg);border-color:var(--fg);color:var(--now)}
  main .rail-nav button:focus-visible{outline:2px solid var(--pacific);outline-offset:2px}
  main .rail-nav button[data-end="1"]{opacity:.3;pointer-events:none}
  /* Head and rail now share one gutter and no max-width, so the title sits at
     the far left and the arrows at the far right — the row spans the screen
     instead of being penned into a centred 1180px column. Same value on both,
     so the first card's left edge and the H of "Six Scopes" are on one line. */
  main .rail{--pad:clamp(24px,6vw,96px);
    display:flex;gap:20px;align-items:stretch;overflow-x:auto;overflow-y:hidden;
    scroll-snap-type:x proximity;overscroll-behavior-x:contain;
    padding:44px 0 10px var(--pad);scroll-padding-left:var(--pad);
    scrollbar-width:none;-ms-overflow-style:none}
  main .rail::-webkit-scrollbar{display:none}
  main .card{position:relative;flex:0 0 auto;width:clamp(258px,23vw,380px);aspect-ratio:3/4;
    border-radius:var(--r-lg);overflow:hidden;scroll-snap-align:start;cursor:pointer;contain:layout style paint;
    background:linear-gradient(165deg,var(--imperial),var(--stage));
    box-shadow:0 16px 40px rgba(3,15,38,.22);
    /* box-shadow is deliberately NOT transitioned. Cross-fading a 74px blur
       across six cards repaints a large area every frame, and it was doing that
       for 550ms on every hover — including hovers that happen while the page is
       scrolling. The shadow now changes instantly, which is invisible underneath
       a 550ms lift. Only `translate` animates, and that is compositor-only. */
    transition:translate var(--mo-slow) cubic-bezier(.22,1,.36,1)}
  /* Lift uses the individual `translate` property, not `transform`. The fan
     animation writes transform inline every frame, so a transform-based hover
     would simply be overwritten. These two compose instead of colliding. */
  main .card:hover,main .card:focus-visible{translate:0 -10px;
    box-shadow:0 34px 74px rgba(3,15,38,.34);outline:none}
  main .card-img{position:absolute;inset:0;background-size:cover;background-position:center;
    filter:grayscale(1) contrast(1.06);mix-blend-mode:luminosity;opacity:.92;transform:scale(1.02);
    transition:transform 1.15s cubic-bezier(.22,1,.36,1)}
  main .card:hover .card-img{transform:scale(1.11)}
  main .card-veil{position:absolute;inset:0;pointer-events:none;
    background:linear-gradient(180deg,rgba(3,15,38,0) 30%,rgba(3,15,38,.55) 100%)}
  main .card-sheen{position:absolute;inset:0;pointer-events:none;opacity:0;transform:translateX(-40%);
    background:linear-gradient(112deg,transparent 38%,rgba(127,208,215,.16) 50%,transparent 62%);
    transition:opacity var(--mo-move) ease,transform 1.4s cubic-bezier(.22,1,.36,1)}
  main .card:hover .card-sheen{opacity:1;transform:translateX(40%)}
  main .card-glass{position:absolute;left:12px;right:12px;bottom:12px;padding:15px 16px 16px;
    border-radius:var(--r-md);background:rgba(247,247,243,.09);
    backdrop-filter:blur(20px) saturate(1.45);-webkit-backdrop-filter:blur(20px) saturate(1.45);
    border:1px solid rgba(247,247,243,.2);
    transition:background var(--mo-move) ease,border-color var(--mo-move) ease}
  main .card:hover .card-glass{background:rgba(3,15,38,.42);border-color:rgba(127,208,215,.38)}
  /* Above .card-glass, which is the last positioned child and would otherwise
     take the click on the caption. The card's own :hover still drives the
     image, sheen and caption because the anchor is inside the article. */
  main .card-link{position:absolute;inset:0;z-index:3}
  main .card-link:focus-visible{outline:3px solid var(--mint);outline-offset:-6px;border-radius:inherit}
  main .card-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
  main .card-head h4{margin:0;font-size:19px;font-weight:700;letter-spacing:-.015em;
    line-height:1.2;color:var(--porcelain)}
  main .card-head span{flex:none;font-size:17px;color:var(--mint);opacity:0;transform:translateX(-8px);
    transition:opacity var(--mo-move) ease .08s,transform var(--mo-slow) cubic-bezier(.22,1,.36,1) .08s}
  main .card:hover .card-head span{opacity:1;transform:none}
  /* The panel opens on a slow curve again. Height is a layout property, so this
     does cost a relayout per frame — but two things make that safe now that
     weren't true before. `contain:layout style paint` on the card scopes the
     relayout to inside that one card instead of the whole six-item row, and the
     scroll gate below switches every card transition off while the page is
     moving, so this can only ever run against a stationary cursor. It resizes
     in one step during a scroll and eases open when you have actually stopped.
     The delay on the hover state is deliberate: it stops the panel firing when
     the cursor is only passing across the rail on its way somewhere else. */
  main .card-glass p{margin:0;max-height:0;opacity:0;overflow:hidden;font-size:13.5px;
    line-height:1.55;color:rgba(247,247,243,.86);transform:translateY(8px);
    transition:max-height var(--mo-slow) cubic-bezier(.22,1,.36,1),margin-top var(--mo-slow) cubic-bezier(.22,1,.36,1),opacity var(--mo-move) ease,transform var(--mo-slow) cubic-bezier(.22,1,.36,1)}
  main .card:hover .card-glass p{max-height:230px;opacity:1;margin-top:10px;transform:none;
    transition-delay:.11s}
  @media (hover:none){
    main .card-glass p{max-height:230px;opacity:1;margin-top:10px}
    main .card-head span{opacity:1;transform:none}
  }
  @media (max-width:760px){
    main .rail{gap:14px}
    main .card{width:min(76vw,300px)}
    main .rail-nav button{width:40px;height:40px}
  }
  /* ── What We Do: stacked, then broken out ────────────────────────
     The section holds 200vh of scroll with the composition pinned across it.
     At the top of that run every card is transformed back onto the first
     card's position — a fanned pile. As it plays they return to zero, which
     is their natural rail position, so the break-out lands them exactly where
     the layout already put them and nothing has to be positioned twice. */
  /* While the page is scrolling, the rail stops accepting the pointer and every
     card transition is switched off. Hover states can't start, can't animate,
     and any state already showing is dropped in one frame instead of easing out.
     Vertical scroll therefore costs the same whether the cursor happens to be
     over a card or not. Both come straight back when scrolling stops, so a
     stationary cursor still gets the full hover. */
  html[data-scrolling] main .rail{pointer-events:none}
  html[data-scrolling] main .card,
  html[data-scrolling] main .card *{transition:none}
  /* Normal height. This was 200vh with a sticky .svc-pin, which existed only
     to give the fanned-pile break-out somewhere to play out; with the stack
     removed there is nothing to pin and the section is just a section. */
  main #services{display:block;padding-left:0;padding-right:0}
  /* `main section > div` sets max-width:1100px on any direct div child, and
     .svc-pin is one — so the whole composition, rail included, was being boxed
     into a centred 1100px column. That is why the cards stopped short of the
     right edge. Still released here so the rail stays genuinely full-bleed. */
  main #services .svc-pin{max-width:none;margin:0}
  main #services .wide{max-width:none}
  /* A horizontal scroller from the moment it exists. It used to be locked
     until the pile finished assembling, because a horizontal scroll mid-fan
     fought the transforms and snap points on moving elements read as stutter.
     Neither applies now, so the data-open gate is gone with them. */
  main #services .rail{overflow-x:auto;scroll-snap-type:x proximity}
  main #services .rail-nav{transition:opacity var(--mo-move) ease}
  main #services .wide{padding:0 clamp(24px,6vw,96px)}
  main .eyebrow{margin:0 0 14px;font-size:11.5px;font-weight:700;letter-spacing:.14em;
    text-transform:uppercase;color:var(--fg-accent);transition:color var(--mo-ui) ease}
  main hr{border:0;border-top:1px solid var(--edge);margin:0 clamp(24px,6vw,96px)}

  @media (max-width:1080px){.cap nav,.cta{display:none}.burger{display:inline-flex}}
  @media (max-width:760px){
    /* SHORTER THAN THE DESKTOP RUN, AND NOT ONLY BECAUSE PHONES SCROLL FAST.
       The desktop length is set by the typing: three lines written a character
       at a time need scroll distance or the pace becomes unreadable. On narrow
       the two supporting lines arrive WHOLE (see the typeTo calls in
       lc-hero.js), and a line that is already there needs less room than one
       being written — so the same beats survive a shorter run.
       330vh is a 16% cut on 392vh. It cannot go much below that without the
       reading windows getting tight.
       330 -> 280 CAME FROM THE GAPS, NOT THE READING. A quarter of the mobile
       hero was dead scroll — 454px of 1941, including a 250px stretch between
       the first and second beats showing an array and no words. Narrow now has
       its own beat windows with those gaps closed (see updateHero), so this is
       22% shorter again while every beat is on screen LONGER than it was at
       392vh. Re-measure with the breakdown in that comment if it moves. */
    #wrap{height:280vh}
    /* ── THE STAGE STOPS FILLING THE WINDOW ────────────────────────
       Everywhere else the hero is a full-viewport takeover; on a phone that
       means the array runs off the bottom edge with no floor under it, and the
       composition has no bottom at all.

       TWO THINGS HAD TO CHANGE TOGETHER. #stage carries the dark gradient, and
       #wrap is transparent — so the moment the stage is shorter than the
       window, the strip below it falls through to #ground, which is Porcelain
       for the whole of the hero (the journey's first stop is at q .262, long
       after). A short stage on its own puts a white band under the hero. #wrap
       therefore takes the gradient's own end colour and the strip reads as the
       hero continuing rather than as a gap.

       svh, NOT vh, AND THAT IS THE OTHER HALF OF IT. #stage was the only
       pinned block in this file still on vh — #record and #approach both use
       svh. On iOS vh is the LARGE viewport, the one with the URL bar hidden,
       so the stage was taller than what you can actually see and the bottom of
       every frame sat under the browser chrome. svh is the small viewport, and
       the usual objection to it — that the stage leaves a gap when the bar
       collapses — is exactly what the #wrap backing above now absorbs. */
    #wrap{background:var(--prussian)}
    #stage{height:86svh}
    .stmt h1,.stmt h2{font-size:clamp(27px,7.9vw,43px);line-height:1.15;letter-spacing:-.02em}
    .lead h1,.stmt.sub h2{font-size:clamp(25px,7.5vw,41px);line-height:1.1;
      letter-spacing:-.025em}
    .low{padding-top:26vh}
    .burger{padding:12px}
  }
  @media (prefers-reduced-motion:reduce){
    .stmt,header,#hphoto{transition:none}
  }

  /* ── WordPress integration ────────────────────────────────────────
     Everything above this line is the prototype's stylesheet. Everything
     below exists only because the page is now served by WordPress. */

  /* The admin bar is itself position:fixed at top 0, and it pushes <html>
     down with a !important margin. The cap is fixed at top 0 too, so for a
     logged-in editor the two occupy the same 32px. Nothing else needs to
     move: #ground is fixed to the viewport and still covers it, and the
     drawer is a full-height panel that simply starts lower. */
  .admin-bar header{top:32px}
  .admin-bar #mnav{top:32px}
  @media screen and (max-width:782px){
    .admin-bar header{top:46px}
    .admin-bar #mnav{top:46px}
  }

  /* #after's -56vh only makes sense with a hero above it to ride up over.
     On any other template there is nothing there, so the overlap would drag
     the first section up under the fixed nav. Cancelled, and the top corners
     go with it — they are the lip that arrives over the photograph, and with
     no photograph they read as a stray notch. The BOTTOM corners stay: they
     are the lip that lifts off the footer, which every page still does. */
  /* #after NO LONGER RESERVES THE CAP. It used to, and so did the masthead —
     122px here plus 105px there was 227px of empty page above the eyebrow on
     every interior page, which read as a failed load rather than as a wide
     opening. Only two things can be first on an interior page, .lc-masthead
     and .lc-generic, and each now reserves the cap itself, so it is paid for
     exactly once wherever the page starts. */
  body:not(.lc-front) #after{margin-top:0;padding-top:0;
    border-top-left-radius:0;border-top-right-radius:0}
  body:not(.lc-front) main .lc-generic{min-height:60vh;
    padding:calc(var(--head-h) + 40px) clamp(24px,6vw,96px) 0}
  body:not(.lc-front) main .lc-generic .mid{text-align:left}
  body:not(.lc-front) main .lc-generic .lede{margin-left:0;margin-right:0}

  /* ── the Welcome Video's sound control ────────────────────────────
     TWO STATES, ONE BUTTON.

     BEFORE: big, centred, a play triangle and a "hear from the founder" label. It
     has to read as an invitation, because the film autoplays MUTED and a
     visitor has no way of knowing there is a voice on it. A discreet corner
     pill was the first attempt and it was wrong for exactly one reason: the
     default outcome of a control nobody notices is that nobody ever hears
     the founder, which is the same as not having made the film.

     AFTER: small, cornered, a speaker glyph. Once the sound is on it really is
     just a mute toggle, and a large centred button sitting over the picture
     would then be in the way of the thing it started.

     Position is switched rather than transitioned. It changes at the same
     moment the film restarts and the label changes, so an animated flight
     across the card would be motion competing with the cut it belongs to. */
  main .app-sound{position:absolute;z-index:3;cursor:pointer;
    display:inline-flex;align-items:center;
    color:var(--porcelain);font-family:'DM Sans',system-ui,sans-serif;
    -webkit-backdrop-filter:blur(14px) saturate(1.2);backdrop-filter:blur(14px) saturate(1.2);
    transition:background var(--mo-ui) ease,border-color var(--mo-ui) ease,transform var(--mo-ui) cubic-bezier(.22,1,.36,1)}
  main .app-sound:focus-visible{outline:2px solid var(--pacific);outline-offset:4px}
  main .app-sound svg{display:block;fill:currentColor;stroke:currentColor;
    stroke-width:1.7;stroke-linecap:round}
  main .app-sound svg path{fill:none}
  main .app-sound .ic-play{fill:currentColor;stroke:none}
  main .app-sound .ic-play path{fill:currentColor}
  main .app-sound .ic-spk path:first-child{fill:currentColor;stroke-width:1}
  /* The label is three words and must stay on one line. It was breaking to two
     lines at 360-390px and to three at 320 — every common iPhone width below
     the Pro Max — which turned a pill into a stack and read as a mistake. */
  main .app-sound-t{white-space:nowrap}

  /* the invitation */
  main .app-sound:not(.on){left:50%;top:50%;transform:translate(-50%,-50%);
    gap:14px;padding:14px 26px 14px 16px;border-radius:var(--r-pill);
    font-size:16px;font-weight:700;letter-spacing:-.005em;
    background:rgba(3,15,38,.58);border:1px solid rgba(247,247,243,.34);
    box-shadow:0 18px 50px rgba(3,15,38,.4)}
  main .app-sound:not(.on):hover{background:rgba(3,15,38,.74);
    border-color:rgba(247,247,243,.52);transform:translate(-50%,-50%) scale(1.035)}
  main .app-sound:not(.on) .app-sound-i{display:grid;place-items:center;
    width:38px;height:38px;border-radius:50%;background:var(--pacific);flex:none}
  main .app-sound:not(.on) svg{width:19px;height:19px}
  main .app-sound:not(.on) .ic-play{margin-left:2px}   /* optical centring of a triangle */
  main .app-sound:not(.on) .ic-spk{display:none}
  /* ── OFF THE FACE, ON A PHONE (client, 14 Aug 2026) ────────────────
     Dead centre is right on a desktop card, where the pill is small against a
     wide frame and lands on background. On a phone the card is 385px and the
     same centre point is the middle of whoever is on screen — the button sits
     on a face. Moved to the lower portion, which is also where a play control
     is expected and where the thumb already is.
     The hover transform has to be restated: it repeats the translate, so
     leaving it would snap the pill back to the vertical centre on touch. */
  @media (max-width:760px){
    main .app-sound:not(.on){top:auto;bottom:clamp(16px,4.5vh,34px);
      transform:translate(-50%,0)}
    main .app-sound:not(.on):hover{transform:translate(-50%,0) scale(1.035)}
  }

  /* the mute toggle */
  main .app-sound.on{left:clamp(14px,2vw,26px);bottom:clamp(14px,2vw,26px);
    gap:9px;padding:9px 16px 9px 12px;border-radius:var(--r-pill);
    font-size:13.5px;font-weight:600;
    background:var(--pacific);border:1px solid transparent;color:#fff}
  main .app-sound.on:hover{transform:translateY(-2px)}
  main .app-sound.on svg{width:17px;height:17px}
  main .app-sound.on .ic-play{display:none}
  main .app-sound.on .app-sound-t{display:none}

  @media (max-width:760px){
    main .app-sound:not(.on){font-size:14px;padding:11px 20px 11px 13px;gap:11px}
    main .app-sound:not(.on) .app-sound-i{width:32px;height:32px}
  }
  @media (prefers-reduced-motion:reduce){
    main .app-sound{transition:none}
    main .app-sound:not(.on):hover{transform:translate(-50%,-50%)}
  }

  /* ── ITEM B: scope thumbnail treatments ───────────────────────────
     a note is "not loving the navy duotone on the six images" and asked what
     other options there are. Four alternatives, switched by the data-thumb
     attribute on the CARDS' CONTAINER so any of them can be put in front of
     her without touching anything else.

     THESE ARE NOT SCOPED TO #rail ANY MORE. They were, and the What We Do
     index — which now renders the same .card as the homepage — sat outside
     that ID and fell back to the default duotone, which is the exact treatment
     she asked to move away from. Attribute only, so every surface carrying
     cards opts in identically. There are two, and BOTH must hold the same
     value or the two pages disagree: #rail in template-parts/home/services.php
     and .lc-scopes in inc/blocks.php.

     The duotone itself is the DEFAULT and is unchanged — it is the base
     .card / .card-img rule further up. The card supplies hue and chroma, the
     photograph supplies luminance, and mix-blend-mode:luminosity marries the
     two. That is why the images read as one set; it is also why they read as
     heavily navy, which is the complaint.

     Once she picks, delete the other rules and the attribute.              */

  /* 1. Untreated. Full-colour photography, no blend — the veil alone carries
        the caption's legibility. Most honest to the sites, least cohesive as
        a set, because six photographs shot in six conditions stop agreeing
        with each other the moment nothing is unifying them. */
  main [data-thumb="full-colour"] .card{background:var(--stage)}
  main [data-thumb="full-colour"] .card-img{filter:none;mix-blend-mode:normal;opacity:1}

  /* 2. Lighter tint. Same mechanism, a paler ground and a brightness lift, so
        cohesion survives without the images going navy-black. This is the
        most conservative answer to what she actually asked for. */
  main [data-thumb="tint"] .card{background:linear-gradient(165deg,#12468F,#0C2E63)}
  main [data-thumb="tint"] .card-img{filter:grayscale(1) contrast(.96) brightness(1.12);opacity:.86}

  /* 3. Monochrome at rest, full colour on hover. The set is unified when
        scanned and the individual site is revealed on approach. Note the
        filter transition is deliberately added here and nowhere else — the
        scroll gate (html[data-scrolling] .card *) still switches it off while
        the page is moving, so it can only ever run against a still cursor. */
  main [data-thumb="mono-hover"] .card{background:#0A1B2E}
  main [data-thumb="mono-hover"] .card-img{filter:grayscale(1) contrast(1.04);
    mix-blend-mode:normal;opacity:.85;
    transition:transform 1.15s cubic-bezier(.22,1,.36,1),filter var(--mo-move) ease,opacity var(--mo-move) ease}
  main [data-thumb="mono-hover"] .card:hover .card-img{filter:grayscale(0) contrast(1);opacity:1}

  /* 4. Neutral at rest, treatment on hover — the inverse of 3. The rail reads
        as photography and the brand colour is what happens when you engage
        with it. The most restrained of the four at a glance, and the one that
        makes the duotone feel intentional rather than applied. */
  main [data-thumb="duotone-hover"] .card{background:linear-gradient(165deg,var(--imperial),var(--stage))}
  main [data-thumb="duotone-hover"] .card-img{filter:none;mix-blend-mode:normal;opacity:1;
    transition:transform 1.15s cubic-bezier(.22,1,.36,1),filter var(--mo-move) ease,opacity var(--mo-move) ease}
  main [data-thumb="duotone-hover"] .card:hover .card-img{filter:grayscale(1) contrast(1.06);
    mix-blend-mode:luminosity;opacity:.92}

  /* ── INTERIOR PAGES ───────────────────────────────────────────────
     Everything below styles the block vocabulary in inc/blocks.php. It is
     deliberately in the non-scrubbed idiom: ordinary section heights, no
     pins, no chambers, entrance on a one-way latch. If item D lands and the
     scroll pattern is withdrawn, this is already the house style rather than
     a thing that has to be invented under time pressure.

     None of these sections paint a background. The ground still shows
     through on every page — that invariant is not homepage-only. */

  main .lc-sec{display:block;min-height:0;padding:var(--sp-sec) 0;position:relative}
  /* THE EXPANDING MASTHEAD PAYS NO BOTTOM PADDING, so the section under it got
     half the air every other section boundary gets — measured on Leadership,
     68px against the 140px between every later pair. Katie read that as the
     spacing being uneven and asked for more above "The Experience Behind the
     Build", which is exactly this gap. Restoring the missing half here fixes it
     wherever the expanding opener is used rather than on one page. */
  main .lc-mast-x + .lc-sec{padding-top:calc(var(--sp-sec) * 2)}
  main .lc-in{max-width:1180px;margin:0 auto;width:100%;padding:0 clamp(24px,6vw,96px)}
  /* `main section > div` caps direct div children at 1100px; .lc-in sets its
     own wider measure and has to win. */
  main .lc-sec > .lc-in{max-width:1180px}

  /* ── the interior masthead ────────────────────────────────────────
     The page's hero. See louth_callan_block_masthead() for why it is shaped
     this way; what matters here is the two-column band and the fact that the
     cap is reserved exactly ONCE — #after used to reserve it as well, which
     put 227px of empty page above the eyebrow. */
  /* TWO CLASSES IN THE SELECTOR, and it has to stay that way. `main
     section:first-child` further up sets 13vh/8vh for The Record's deck, and
     on an interior page the masthead IS the first section — so that rule
     applied here too, and at (0,1,2) it out-specifies a plain
     `main .lc-masthead` at (0,1,1). Every padding value below was silently
     dead until this selector went to (0,2,1). Worth knowing before anyone
     "simplifies" it: the symptom is not a parse error, it is these numbers
     doing nothing at all.

     TOP. The added term is breathing room AND cover for --head-h, which is
     documented as "14px padding + a 68px cap" and set to 82 — but the padding
     applies top and bottom, so the header actually measures 96 and every
     consumer of the variable is 14px short. It showed up on Careers: longest
     title in the set, copy taller than the band's min-height, so `safe
     center` correctly pins it to the top, and the eyebrow landed 9px under
     the cap. A 36px floor makes the thinnest possible clearance 22px.
     --head-h itself is left alone deliberately — three pinned homepage
     sections are tuned against its current value and the ground journey's
     stops are solved against measured section positions, so correcting it is
     a homepage retune rather than a one-line change.

     BOTTOM is not breathing room at all, it is THE FIELD'S LANE. Run through
     the middle of the band, the flow field crosses the lede, and a dot field
     behind body copy is noise however pale it is. Sat in its own strip below
     everything — clear of the copy, clear of the plate — it reads as a
     baseline trace under the masthead instead. lc-sections.js measures this
     padding and centres the field in it, so changing the number here moves
     the field with it and there is nothing to keep in sync. */
  main .lc-sec.lc-masthead{padding-top:calc(var(--head-h) + var(--sp-open));
    padding-bottom:var(--sp-open-b);overflow:hidden}
  /* WIDOWS WITHOUT UNBREAKABLE TOKENS. `balance` evens the line lengths across
     a heading, which removes the lone last word the audit flagged on About,
     Insights and Careers — and unlike a non-breaking space it can still wrap,
     so a narrow column shrinks the lines instead of overflowing them. Browsers
     without support wrap normally and lose nothing but the tidying. */
  main h1,main h2,main h3,main .lc-x-title h1,main .lc-seq-copy h1,
  main .lc-phero-copy h1{text-wrap:balance}
  main .lc-masthead h1{margin:0;font-size:var(--t-h1);font-weight:700;
    letter-spacing:-.035em;line-height:1.02;color:var(--fg);max-width:18ch;
    transition:color var(--mo-ui) ease}
  main .lc-masthead .lede{margin:26px 0 0;max-width:54ch;font-size:17.5px;
    line-height:1.6;color:var(--fg-dim);transition:color var(--mo-ui) ease}

  /* The field runs the full width of the band, behind everything, and is
     painted only while the band is on screen. It is BELOW the copy in the
     stacking order and takes no pointer events, so it can never interfere
     with reading or with selecting text. */
  main .lc-mast-flow{position:absolute;inset:0;width:100%;height:100%;
    pointer-events:none;z-index:0}
  main .lc-masthead > .lc-in{position:relative;z-index:1}

  /* The band has a MINIMUM HEIGHT whether or not it has a photograph, and the
     copy is centred in it. Without that the six pages still waiting on
     photography start their eyebrow 9px under the fixed cap — the reserve is
     technically satisfied and it still reads as jammed against the nav. This
     also keeps the opener the same size across the set, so moving between an
     illustrated page and a bare one is not a jump. */
  main .lc-mast{display:grid;align-items:center;min-height:clamp(280px,38vh,430px);
    /* SAFE centring, declared twice on purpose. Centred content that is TALLER
       than the box it is centred in overflows symmetrically — it grows past
       the top as well as the bottom — and the top of this box is right under
       a fixed header. Careers has the longest lede in the set and did exactly
       that: 9px of clearance where every other page had 20-plus, i.e. the
       eyebrow tucked under the cap on the one page with the most to say.
       `safe` pins the start edge and lets the overflow go downward only.
       Browsers without it keep the plain `center` above rather than dropping
       the pair and falling back to `stretch`. */
    align-items:safe center}

  /* One column until there is a photograph to put beside the copy. `stretch`
     rather than a fixed aspect on the plate is the whole trick: the media
     column takes the band's height, the image is `cover`, and so a 3:4 scope
     aerial and a 16:9 project frame both sit correctly in the same slot with
     no per-image tuning. */
  main .lc-masthead[data-media="1"] .lc-mast{align-items:stretch;
    grid-template-columns:minmax(0,7fr) minmax(0,5fr);gap:clamp(30px,4.5vw,68px);
    min-height:clamp(300px,42vh,470px)}
  main .lc-masthead[data-media="1"] .lc-mast-copy{align-self:center}
  main .lc-masthead[data-media="1"] h1{max-width:14ch}
  main .lc-masthead[data-media="1"] .lede{max-width:44ch}
  /* 'wide': the photograph takes the larger column and the band grows with it,
     so the copy sits further left against a bigger picture. The measure on the
     headline tightens to match — the same words over a narrower column would
     otherwise run to four short lines. */
  main .lc-masthead.lc-mast--wide[data-media="1"] .lc-mast{
    grid-template-columns:minmax(0,5fr) minmax(0,7fr);
    min-height:clamp(340px,50vh,560px)}
  /* No max-width. 13ch at 84px is about 470px and this column is 393px, so the
     measure was wider than the track it sits in — it constrained nothing and
     read as if it did. The column is the measure here. */
  main .lc-mast--wide[data-media="1"] h1{max-width:none}
  main .lc-mast--wide[data-media="1"] .lede{max-width:34ch}
  /* 'flip': photograph first. Source order is unchanged, so the page still
     reads heading-then-picture to a screen reader and on a phone.
     The tracks mirror with it. `order` moves the media into column ONE, which
     is the 7fr track — so without this the flip silently handed the picture
     the larger share and Careers ended up with a wider opener than Projects,
     which is the page that was supposed to have the big one. */
  main .lc-masthead.lc-mast--flip[data-media="1"] .lc-mast{
    grid-template-columns:minmax(0,5fr) minmax(0,7fr)}
  main .lc-mast--flip[data-media="1"] .lc-mast-copy{order:2}
  main .lc-mast--flip[data-media="1"] .lc-mast-media{order:1}
  main .lc-mast-media{position:relative;border-radius:var(--r-lg);overflow:hidden;
    min-height:clamp(260px,36vh,420px);
    background:rgba(8,39,103,.10);
    box-shadow:0 26px 60px rgba(3,15,38,.22)}
  main .lc-mast-media img{position:absolute;inset:0;display:block;
    width:100%;height:100%;object-fit:cover;
    object-position:var(--imgpos,50% 50%)}
  @media (max-width:900px){
    /* Copy first on a phone — the title is what the reader came for, and a
       plate above it pushes it below the fold for no gain. */
    /* The two variants are matched selector-for-selector here. They set their
       tracks at (0,4,2) and this breakpoint used to answer at (0,3,2), so the
       phone kept two columns on Projects and Careers: the picture collapsed to
       208px inside a 370px-tall band. A breakpoint that loses to the thing it
       is meant to override is worse than no breakpoint. */
    main .lc-masthead[data-media="1"] .lc-mast,
    main .lc-masthead.lc-mast--wide[data-media="1"] .lc-mast,
    main .lc-masthead.lc-mast--flip[data-media="1"] .lc-mast{
      grid-template-columns:1fr;gap:30px;min-height:clamp(280px,38vh,430px)}
    main .lc-masthead[data-media="1"] h1,
    main .lc-mast--wide[data-media="1"] h1{max-width:none}
    main .lc-mast--wide[data-media="1"] .lede{max-width:44ch}
    /* `width:100%` is load-bearing, not tidiness. A grid item carrying an
       aspect-ratio and no definite inline size is stretched to its column by
       Blink but treated as replaced-like by WebKit, which declines to stretch
       it and instead derives the width from whatever height the row happened
       to get. On a phone that is leftover free space, so the plate collapsed
       to 79px on Projects, 48px on Safety and Contact, and to nothing at all
       on Careers and Insights. A definite width makes the ratio resolve from
       the inline axis in both engines. */
    main .lc-mast-media{min-height:0;aspect-ratio:16/10;width:100%}
    /* The plate changes shape here — 0.73 portrait to a 16:10 band — so a
       portrait source loses roughly three times as much height and the focal
       point has to move with it. Falls back to --imgpos, which falls back to
       centre, so an image that needs neither sets neither. */
    main .lc-mast-media img{object-position:var(--imgpos-m,var(--imgpos,50% 50%))}
  }

  /* ── the expanding opener (About) ─────────────────────────────────
     The expanding opener. Motion in assets/js/lc-expand.js, markup from the
     'expand' branch of louth_callan_block_masthead().

     ── THE BASE STATE IS THE FINISHED HEADER, NOT THE RESTING ONE ────
     Everything in this first group describes a static full-bleed photographic
     header with the headline over it and the lede beneath: no clip, no sticky,
     no extra track, scrim already up, lede already visible. That is what
     no-JS and prefers-reduced-motion get, and it is a composition rather than
     a fallback — it is the frame the animation ends on.

     The animated state is gated on html.lcjs, which lc-sections.js adds ONLY
     when scripts run and motion is allowed. Written this way round because the
     opposite — resting state in CSS, finished state applied by script — leaves
     a reader with reduced motion staring at a 42%-wide plate with the lede
     invisible and no way to open it, and leaves a no-JS reader with a
     1.55-viewport hole under the header where the track would be.

     It cancels .lc-masthead's own padding on purpose: that rule reserves room
     under the fixed nav for a copy band, and this is a full-bleed image that
     is supposed to run under it, exactly as the homepage hero does. */
  main .lc-sec.lc-masthead.lc-mast-x{padding:0;overflow:visible}
  /* ESCAPES THE 1100px MEASURE. `main section > div` caps every direct child
     div at 1100 and centres it — right for every band of copy on the site and
     wrong for the one element whose entire premise is reaching the edges of
     the frame. Without this the "full bleed" state stops 170px short on each
     side at 1440 and reads as a wide plate rather than as the photograph
     taking over. Measured on the render, not assumed. */
  main .lc-mast-x > .lc-x-track{max-width:none;margin:0}
  main .lc-x-track{position:relative;width:100%}
  main .lc-x-stage{position:relative;width:100%;overflow:hidden;
    min-height:clamp(460px,78vh,860px);background:var(--stage)}
  main .lc-x-frame{position:absolute;inset:0;overflow:hidden}
  main .lc-x-media{position:absolute;inset:0;width:100%;height:100%;
    object-fit:cover;transform-origin:center;
    user-select:none;-webkit-user-drag:none}
  /* The middle of this gradient was its weakest point at .14, and the middle is
     exactly where the eyebrow and headline sit. Lifted so the copy has a floor
     under it whatever the photograph is doing there — measured over the old
     What We Do frame the eyebrow band ran at 3.24:1 against white type. */
  main .lc-x-scrim{position:absolute;inset:0;pointer-events:none;opacity:.45;
    background:linear-gradient(to top,rgba(3,15,38,.84),
      rgba(3,15,38,.38) 45%,rgba(3,15,38,.58))}
  /* Above the scrim and the overlay both — the headline is the one thing that
     must never be sat on by the gradient meant to make it legible. */
  main .lc-x-title{position:absolute;inset:0;z-index:2;display:flex;
    flex-direction:column;align-items:center;justify-content:center;
    gap:clamp(10px,1.4vh,18px);text-align:center;
    padding:calc(var(--head-h) + 12px) clamp(20px,6vw,80px) 0;
    pointer-events:none}
  main .lc-x-title .eyebrow{margin:0;color:rgba(255,255,255,.74)}
  /* Its own size ramp rather than .lc-masthead h1's: this one is centred over
     a photograph with the whole viewport to fill, not set beside a plate. */
  main .lc-x-title h1{margin:0;max-width:16ch;color:#fff;
    font-size:var(--t-h1);font-weight:700;line-height:1.02;
    letter-spacing:-.03em;text-shadow:0 2px 24px rgba(3,15,38,.5)}
  /* CENTRED, NOT SAT ON THE FLOOR . This was pinned to
     the bottom edge at lede size, which is the right treatment for a caption
     under a picture and the wrong one here: by the time it fades in, the
     headline has gone and this paragraph IS the frame's content, with the
     whole viewport to itself. Bottom-aligned and small, it read as an
     afterthought under the photograph rather than as the thing being said.
     So it takes the centre the headline vacated, and steps up from a 19px
     lede to a 32px ceiling on a tighter measure — large enough to hold a
     full-bleed frame, short enough per line to stay readable at that size. */
  main .lc-x-overlay{position:absolute;inset:0;z-index:1;display:flex;
    flex-direction:column;align-items:center;justify-content:center;
    text-align:center;padding:calc(var(--head-h) + 12px) clamp(28px,6vw,72px) clamp(28px,6vw,72px)}
  main .lc-x-overlay .lede{margin:0;max-width:34ch;color:#fff;
    font-size:clamp(20px,2.4vw,32px);line-height:1.42;letter-spacing:-.012em;
    text-shadow:0 2px 22px rgba(3,15,38,.6)}
  /* ── AND THE SAME VEIL THE SEQUENCED OPENER GOT (14 Aug 2026) ──────
     .lc-x-scrim above is the ONLY floor this copy had, and it is switched off
     by html.lcjs — so in the normal case the type carried nothing but its own
     text-shadow, and legibility was a property of whichever photograph was in
     the slot. Measured on the new About hero: h1 worst-case 1.66:1, lede
     1.62:1, because the sunset sits behind part of the centred copy. Medians
     were 6.5-7:1, which is why this survives a glance and fails a reader.

     Same radial wash as .lc-seq-copy::before, for the same reason and with the
     same shape — strongest under the type, gone by the corners, no edge to
     read as a panel. Kept on BOTH title and overlay because they cross-fade:
     veiling one would make the handover a brightness step.

     NOT on Leadership's white phase. .lc-mast-x--light puts a NAVY headline on
     a white stage while JS is running (see the light-opener block below), so a
     dark wash there would be a smudge behind dark type on white. Under reduced
     motion that variant falls back to white type over the photograph — the
     About case — and the veil correctly applies. */
  main .lc-x-title::before,
  main .lc-x-overlay::before{content:'';position:absolute;inset:0;z-index:-1;
    pointer-events:none;
    background:radial-gradient(62% 50% at 50% var(--veil-y,50%),
      rgba(3,15,38,.72) 0,
      rgba(3,15,38,.50) 48%,
      rgba(3,15,38,.16) 74%,
      rgba(3,15,38,0) 92%)}
  html.lcjs main .lc-mast-x--light .lc-x-title::before{display:none}
  /* --veil-y FOLLOWS THE TYPE, and under reduced motion the type moves. The
     static composition below splits the stage in half and pushes the title to
     the BOTTOM of its box and the lede to the TOP of its box — so a veil
     centred at 50% of each box sits above one and below the other. Measured:
     the h1 stayed at 1.87:1 with the veil on, because it was reading the
     gradient's falloff instead of its core. These two lines put each core back
     under its own text; everything else about the wash is unchanged. */
  html:not(.lcjs) main .lc-x-title::before{--veil-y:82%}
  html:not(.lcjs) main .lc-x-overlay::before{--veil-y:18%}
  /* AND THE SCRIM ITSELF, in the one mode where it is on. .lc-x-scrim is only
     ever painted without JS, and at opacity .45 over a .38 middle stop its
     effective alpha there was .17 — a rumour of a scrim. That was survivable
     while this component held a snow aerial and is not now: reduced motion
     puts BOTH blocks of copy on one static full-bleed frame, so the title sits
     in the top half, which is where this photograph keeps its sky.
     Raised to a real floor and the opacity folded into the stops, so what is
     written is what is painted. The radial above still does the local work;
     this is the base it works from. */
  html:not(.lcjs) main .lc-x-scrim{opacity:1;
    background:linear-gradient(to top,rgba(3,15,38,.62),
      rgba(3,15,38,.46) 45%,rgba(3,15,38,.56))}
  /* No scroll to hint at until there is an expansion to drive. */
  main .lc-x-hint{display:none}

  /* ── the static composition ───────────────────────────────────────
     WHAT A READER WITH REDUCE MOTION ON ACTUALLY SEES, and until 13 Aug 2026 it
     was two paragraphs printed on top of each other.

     lc-expand.js's header says "the stylesheet's base state is the finished
     header, so not starting is a complete rendering, not a broken one." That was
     never true for this component. .lc-x-title and .lc-x-overlay are BOTH
     position:absolute inset:0 and BOTH flex-centred, because the animation
     cross-fades between them — one is always at opacity 0 while the other is
     read. Take the animation away and they occupy the same centred box, so the
     headline lands in the middle of the lede.

     It hid because html.lcjs is never added under reduced motion, so every
     animated rule — including the opacity:0 that separates the two layers — is
     inert. Chrome and WebKit both render it correctly with motion allowed,
     which is why a full cross-engine sweep missed it four times: the fault is
     in a MEDIA STATE, not a browser.

     The fix is composition, not opacity: the two layers split the stage instead
     of both taking all of it. The headline sits in the top half bottom-aligned,
     the paragraph in the bottom half top-aligned, and they meet at the middle —
     so it reads as one centred stack, which is what the section says once it has
     finished animating anyway.

     SPLIT RATHER THAN REFLOWED, and that is not a style preference.
     .lc-x-overlay is a child of .lc-x-frame, not of the stage — the frame
     carries the clip-path, so the paragraph is clipped by the expanding window
     along with the photograph. Setting both layers to position:static therefore
     does NOT stack them: the overlay flows from the top of the absolutely
     positioned frame while the title centres in the stage, and the paragraph
     lands 362px ABOVE the headline. Measured; it was the first thing tried. */
  html:not(.lcjs) main .lc-x-title{inset:0 0 50% 0;justify-content:flex-end;
    padding-bottom:clamp(7px,1vh,11px)}
  html:not(.lcjs) main .lc-x-overlay{inset:50% 0 0 0;justify-content:flex-start;
    padding-top:clamp(7px,1vh,11px)}
  /* Steps back down: 32px is right for a paragraph that has the whole viewport
     to itself after the headline has gone, and too loud directly under it. */
  html:not(.lcjs) main .lc-x-overlay .lede{font-size:clamp(17px,1.6vw,21px);
    line-height:1.5;max-width:46ch}

  /* ── the animated state ───────────────────────────────────────────
     The resting inset is the component's own: 42% wide and 58% tall centre
     the frame at 29% left/right and 21% top/bottom. Declared here as well as
     computed in script so the first paint is already the resting frame — set
     only by JS it would flash full bleed for a frame before the first tick.
     The track's height is script's alone, because it depends on the viewport. */
  html.lcjs main .lc-x-stage{position:sticky;top:0;height:100vh;min-height:0}
  html.lcjs main .lc-x-frame{clip-path:inset(21% 29% 21% 29% round 24px);
    will-change:clip-path}
  html.lcjs main .lc-x-media{transform:scale(1.35);will-change:transform}
  html.lcjs main .lc-x-scrim{opacity:0}
  html.lcjs main .lc-x-overlay{opacity:0;pointer-events:none;
    will-change:opacity,transform}
  html.lcjs main .lc-x-title{will-change:opacity,transform}
  html.lcjs main .lc-x-hint{display:block;position:absolute;left:0;right:0;
    bottom:clamp(18px,3vh,34px);z-index:2;text-align:center;pointer-events:none;
    font-size:11px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;
    color:rgba(255,255,255,.62)}
  /* Matches lc-expand.js's NARROW_W/NARROW_H (78/42 -> 11% and 29% insets) so
     the pre-script paint is the phone's resting frame, not the desktop's. The
     two must move together; the script is authoritative from its first tick. */
  @media (max-width:760px){
    html.lcjs main .lc-x-frame{clip-path:inset(29% 11% 29% 11% round 24px)}
    html.lcjs main .lc-mast-x--light .lc-x-frame{clip-path:inset(12% 12% 12% 12% round 24px)}
    html.lcjs main .lc-mast-x--light .lc-x-media{transform:scale(.76)}
  }

  /* ── the light opener: a statement, then the picture ──────────────
     Leadership's variant. Everything here is gated on html.lcjs and that is
     the point rather than a detail: the white screen is the FIRST FRAME of an
     animation, not a design. Without JS or with reduced motion there is no
     scroll to spend on it, so a reader would be left staring at a white box
     with a headline and no way to reach the photograph below it. Ungated,
     this variant would be the one place on the site where turning motion off
     removes content. Gated, it degrades to exactly what About degrades to —
     the finished full-bleed header — and the base rules above already say so.

     THE HEADLINE IS NAVY ON WHITE AND CAN AFFORD TO BE. In this mode the
     expansion has not begun while the line is on screen (see the beat table
     in lc-expand.js), so the type is never over the photograph and needs no
     shadow, no scrim and no light-on-dark fallback. That is a fact enforced
     by the timings, not a hope about them — which is why both live together
     in one table rather than half here and half there.

     LARGER AND ON A WIDER MEASURE than the dark opener's h1. That one shares
     a viewport with a photograph and has to stay out of its way; this one is
     alone on white with the whole stage to fill, and the request was "large,
     uniform" — one size, one weight, one colour, nothing above it. 18ch sets
     the requested line to two lines at desktop rather than three, which is
     what makes it read as a statement instead of a paragraph. */
  /* The group photograph goes with the portraits below it — the client raised
     black-and-white for this frame specifically, and a colour group shot above
     eleven mono portraits would read as the odd one out rather than as the
     page's warm moment. */
  main .lc-mast-x--light .lc-x-media{filter:grayscale(1) contrast(1.06) brightness(1.02)}
  html.lcjs main .lc-mast-x--light .lc-x-stage{background:#fff}
  /* All three numbers fall out of lc-expand.js's START_W = 52: a plate 52% of
     the stage is inset 24% on every side, and the media is scaled by the same
     52% so it fills that plate exactly rather than being cropped into it. Move
     START_W and all three move — the inset twice and the scale once. Same rule
     as the dark pair above; the light plate simply has the extra scale, which
     is what makes it a miniature of the finished frame instead of a window
     onto it. */
  html.lcjs main .lc-mast-x--light .lc-x-frame{opacity:0;
    clip-path:inset(24% 24% 24% 24% round 24px)}
  html.lcjs main .lc-mast-x--light .lc-x-media{transform:scale(.52)}
  html.lcjs main .lc-mast-x--light .lc-x-title h1{color:var(--stage);text-shadow:none;
    max-width:18ch;font-size:var(--t-h1);letter-spacing:-.034em}
  /* The base .lc-x-title rule sets this to white-at-74% for a photograph
     behind it, which on the white stage is white on white. Handing it back to
     --fg-accent puts it on the same token every other eyebrow on the site uses
     rather than inventing a colour for one label — and that token is derived
     from the ground's luminance, so it stays legible without anyone choosing.
     Safe despite the ground moving later in the page: the eyebrow is at zero
     opacity by 30% of the track, long before the journey to Imperial starts. */
  html.lcjs main .lc-mast-x--light .lc-x-title .eyebrow{color:var(--fg-accent)}
  html.lcjs main .lc-mast-x--light .lc-x-hint{color:rgba(4,23,59,.46)}

  /* ── the sequenced opener ─────────────────────────────────────────
     Base state is the finished hero: last frame full bleed, copy visible,
     stack hidden. Everything animated is gated on html.lc-seq, which an
     inline gate adds before first paint only on a session's first visit and
     only when motion is allowed. */
  main .lc-sec.lc-masthead.lc-mast-seq{padding:0;overflow:visible}
  main .lc-mast-seq > .lc-seq-stage{max-width:none;margin:0}
  main .lc-seq-stage{--fw:clamp(132px,16vw,232px);--fh:calc(var(--fw) * 1.25);
    --sh:clamp(460px,78vh,860px);
    position:relative;width:100%;height:var(--sh);overflow:hidden;background:var(--stage)}
  /* Fanned so the stack is visible as it builds — perfectly overlapping
     frames only ever show the topmost one. Offsets are relative to the LAST
     frame, which lands square at 0/0 so it matches the rect the final layer
     opens from. */
  main .lc-seq-f{position:absolute;left:50%;top:50%;width:var(--fw);height:var(--fh);
    --rot:calc((var(--n) - 4) * 4deg);--dx:calc((var(--n) - 4) * 17px);
    object-fit:cover;border-radius:var(--r-md);opacity:0;pointer-events:none;
    box-shadow:0 14px 38px rgba(3,15,38,.42);
    transform:translate(calc(-50% + var(--dx)),-50%) rotate(var(--rot))}
  /* THE WINDOW IS A REAL BOX, NOT A CLIP. It used to be a stage-sized layer
     revealed by clip-path:inset(calc(...)), and that mixed a percentage with px
     lengths inside one clip function. On a Retina display every px length in
     that calc came back halved while the percentage resolved normally, so the
     window opened at 116x575 where it should have been 232x290 — a tall narrow
     slot with the photograph showing through it. Measured off a 2x screenshot;
     it does not reproduce under emulated deviceScaleFactor, only on real
     hardware, which is why it survived the first round of checks.
     Layout resolves width/height against the real box every frame and cannot
     disagree with it, so the geometry is now unconditional. */
  main .lc-seq-final{position:absolute;left:50%;top:50%;width:100%;height:100%;
    transform:translate(-50%,-50%);overflow:hidden}
  /* Stage-sized and centred at every step, so the box grows around a picture
     that never moves — the reveal is a curtain, not a zoom. */
  main .lc-seq-final img{position:absolute;left:50%;top:50%;
    transform:translate(-50%,-50%);width:100vw;height:var(--sh);
    max-width:none;object-fit:cover}
  /* A dark band under the fixed cap. The readability scrim alone tops out at
     an effective ~0.20 alpha, which leaves the nav sitting on whatever the
     photograph happens to be — legible on a bright frame, invisible on a
     dark one. This makes that band deterministic so the nav can simply stay
     light over the stage. */
  main .lc-seq-cap{position:absolute;left:0;right:0;top:0;z-index:1;
    height:clamp(130px,20vh,210px);pointer-events:none;
    background:linear-gradient(to bottom,rgba(3,15,38,.90),rgba(3,15,38,.52) 52%,rgba(3,15,38,0))}
  main .lc-seq-copy{position:absolute;inset:0;z-index:2;display:flex;
    flex-direction:column;align-items:center;justify-content:center;
    gap:clamp(10px,1.4vh,18px);text-align:center;
    padding:calc(var(--head-h) + 12px) clamp(20px,6vw,80px) 0;pointer-events:none}
  /* ── A VEIL UNDER THE COPY, NOT OVER THE PICTURE (14 Aug 2026) ─────
     This stage had a cap band for the nav and two text-shadows, and nothing
     behind the copy itself — so legibility was a property of whichever
     photograph happened to be in the slot. Measured on the new hero, the
     eyebrow scored 1.55:1 and the h1 1.50:1 against white, because the copy is
     centred and the frame's brightest band runs straight through the middle.
     That is the third time this slot has failed the same way, so it is fixed
     at the slot rather than by hunting for a photograph with a dark middle.

     A radial wash, not a rectangle: it has no edge to read as a panel, it is
     strongest exactly where the type is and gone by the corners, so the
     picture keeps its own light everywhere the copy is not. z-index:-1 inside
     .lc-seq-copy's own stacking context puts it under the type and over the
     photograph without another node. It rides the copy's entrance animation
     because it belongs to it. */
  main .lc-seq-copy::before{content:'';position:absolute;inset:0;z-index:-1;
    pointer-events:none;
    background:radial-gradient(62% 50% at 50% 50%,
      rgba(3,15,38,.72) 0,
      rgba(3,15,38,.50) 48%,
      rgba(3,15,38,.16) 74%,
      rgba(3,15,38,0) 92%)}
  /* 74% white is a decorative weight for a label that has to be read over a
     photograph. The eyebrow is the smallest type on the stage at 11.5px. */
  main .lc-seq-copy .eyebrow{margin:0;color:rgba(255,255,255,.94)}
  main .lc-seq-copy h1{margin:0;max-width:16ch;color:#fff;
    font-size:var(--t-h1);font-weight:700;line-height:1.02;
    letter-spacing:-.03em;text-shadow:0 2px 24px rgba(3,15,38,.5)}
  main .lc-seq-copy .lede{margin:0;max-width:52ch;color:rgba(255,255,255,.9);
    font-size:var(--t-lede);line-height:1.5;
    text-shadow:0 2px 18px rgba(3,15,38,.55)}

  /* The animated state. Stack frames arrive at 150ms intervals; the final
     layer opens from the frame's own rect, so the last arrival and the
     expansion are one continuous move rather than a cut. */
  html.lc-seq main .lc-seq-f{
    animation:seqIn .52s cubic-bezier(.22,1,.36,1) both,
              seqOut .32s ease .90s both;
    animation-delay:calc(var(--n) * .15s), .90s}
  html.lc-seq main .lc-seq-final{
    animation:seqGrow .78s cubic-bezier(.65,0,.35,1) .82s both}
  html.lc-seq main .lc-x-scrim{opacity:0;animation:seqFade .5s ease 1.28s both}
  html.lc-seq main .lc-seq-cap{opacity:0;animation:seqCap .5s ease 1.20s both}
  html.lc-seq main .lc-seq-copy{opacity:0;
    animation:seqCopy .62s cubic-bezier(.22,1,.36,1) 1.34s both}
  /* AFTER the animations above, and that is load-bearing: the `animation`
     shorthand resets every animation-* longhand, so a play-state declared
     before it is wiped and the timeline runs while still waiting. Measured —
     the clip reached 0 with only 3 of 6 frames loaded. */
  html.lc-seq-wait main .lc-seq-f,
  html.lc-seq-wait main .lc-seq-final,
  html.lc-seq-wait main .lc-x-scrim,
  html.lc-seq-wait main .lc-seq-cap,
  html.lc-seq-wait main .lc-seq-copy{animation-play-state:paused}
  @keyframes seqIn{
    from{opacity:0;
      transform:translate(calc(-50% + var(--dx)),calc(-50% + 54px)) rotate(var(--rot)) scale(.94)}
    to  {opacity:1;
      transform:translate(calc(-50% + var(--dx)),-50%) rotate(var(--rot)) scale(1)}}
  @keyframes seqOut{to{opacity:0}}
  /* Pure lengths on both ends — no percentage anywhere in the animated path,
     which is what broke it. The end state is the stage's own size stated the
     same way the stage states it, so it lands exactly full bleed. */
  @keyframes seqGrow{
    from{width:var(--fw);height:var(--fh);border-radius:var(--r-md)}
    to  {width:100vw;height:var(--sh);border-radius:0}}
  @keyframes seqFade{from{opacity:0}to{opacity:.45}}
  @keyframes seqCap{from{opacity:0}to{opacity:1}}
  @keyframes seqCopy{
    from{opacity:0;transform:translateY(16px)}
    to  {opacity:1;transform:none}}

  main .lc-sec h2{margin:0 0 18px;font-size:var(--t-h2);font-weight:700;
    letter-spacing:-.028em;line-height:1.08;color:var(--fg);max-width:20ch;
    transition:color var(--mo-ui) ease}
  main .lc-sec h3{margin:0 0 10px;font-size:19px;font-weight:700;letter-spacing:-.015em;
    line-height:1.25;color:var(--fg);transition:color var(--mo-ui) ease}

  /* ── centred prose ────────────────────────────────────────────────
     Same trap as .lc-head-c and the Record's copy: text-align alone leaves
     every child's BOX ranged left with centred text inside it, because the
     heading, the paragraphs and the eyebrow each carry their own measure.
     Each one has to be re-centred with auto margins. The paragraph measure
     also tightens — 66ch of centred text is hard to track back to the start
     of the next line, and a centred block wants to be narrower than a ranged
     one for the same reason a poster does.

     DOUBLED UP AS .lc-prose.lc-prose--c ON PURPOSE. The base rules below set
     `margin:0 0 17px` as a shorthand, which resets margin-left/right to 0 —
     and at equal specificity the later rule wins, so the auto margins were
     being thrown away and the block rendered as centred text in a left-ranged
     box. Measured: heading and both paragraphs sat left. The extra class
     buys a specificity step so source order cannot decide this. */
  main .lc-prose.lc-prose--c{text-align:center}
  main .lc-prose.lc-prose--c h2{margin-left:auto;margin-right:auto;max-width:24ch}
  main .lc-prose.lc-prose--c .eyebrow{margin-left:auto;margin-right:auto;max-width:none}
  main .lc-prose.lc-prose--c p{margin-left:auto;margin-right:auto;max-width:58ch}
  main .lc-prose p{margin:0 0 17px;max-width:66ch;font-size:16.5px;line-height:var(--lh-body);
    color:var(--fg-dim);transition:color var(--mo-ui) ease}
  main .lc-prose p:last-child{margin-bottom:0}
  main .lc-links a{display:inline-block;margin:0 22px 0 0;color:var(--fg-accent);
    text-decoration:none;font-weight:600;border-bottom:1px solid currentColor;
    padding-bottom:1px;transition:color var(--mo-ui) ease,opacity var(--mo-ui) ease}
  main .lc-links a:hover{opacity:.7}

  /* ── entrance ─────────────────────────────────────────────────────
     Same shape as the Leadership rise: hidden ONLY when the script is running
     and motion is welcome. Without .lcjs on <html> everything renders visible,
     so no-JS and reduced-motion never get a blank page. */
  main .lc-rise{transition:opacity var(--mo-slow) cubic-bezier(.22,1,.36,1),transform var(--mo-slow) cubic-bezier(.22,1,.36,1)}
  html.lcjs main .lc-rise{opacity:0;transform:translateY(18px)}
  html.lcjs main .lc-rise.in{opacity:1;transform:none}

  /* ── stats row ────────────────────────────────────────────────────
     Reuses .fig/.num/.cap so the reels work unchanged, but laid out as a flat
     row: no absolute positioning, no fixed height, no deck. The 184px card
     height existed to sidestep a baseline problem in the pinned stack; here
     the cards are in normal flow and can size to their content. */
  /* ── the pinned deck, on any page ──────────────────────────────────
     The homepage Record's composition, lifted out of #record so the Projects
     page can run it too. #record keeps its own rules above — its
     heights are solved against three pinned homepage sections and the ground
     journey's stop table, and re-pointing it at these would have meant
     re-solving both to fix a page that is not broken.

     THE STAGE HEIGHT IS DERIVED HERE, which is the one real improvement over
     the original: 184px for the card plus one 22px lip for every card beyond
     the first, read from --figs. The homepage equivalent is a literal 250px
     with a comment warning that it must be stepped by hand when a figure is
     added — true, and exactly the kind of thing that fails silently. */
  /* 240vh -> 148vh (client, 19 Aug 2026: "a large amount of white space" between
     the record and the map). The composition is a 100vh sticky inside this, so
     the chamber's height beyond that is pinned scroll and the tail is what is
     left over once it unpins — at 240vh that tail was most of a viewport of
     empty ground. The deck solves its own timing against the chamber, so the
     cards simply land over a shorter run. */
  /* KATIE, POST-LAUNCH ROUND 2: still "a large amount of white space" between
     The Record and the map. The chamber's length is what holds the map away —
     the deck's animation ends well before the section does — so shortening it
     brings the map up without touching how the cards stack. */
  main .lc-deck-sec{display:block;height:130vh;min-height:0;padding-top:0;padding-bottom:0}
  /* Horizontal padding matches .lc-in so this section holds the same gutter as
     every other one below the 1100px cap.

     52svh, NOT a full viewport — this is what closes the gap to the map. The
     composition is only ~250px tall, and the space left under it in the pinned
     pane travels with it and reads as dead ground before the card:

         gap = (pane height + bottom padding − composition height) / 2

     A full-height pane gave 344px of it; 52svh gives 111px. Shortening the
     chamber does not touch this term — it only controls how long you look at
     it. The floor is the header cap: at 46svh the composition clears it by 6px,
     so 52svh keeps 33px. The phone keeps 100svh, where the composition is
     top-aligned and taller than this pane would be. */
  main .lc-deck-sec .split{position:sticky;top:0;height:52svh;align-items:center;
    padding:0 clamp(24px,6vw,96px) 2vh;grid-template-columns:minmax(0,5fr) minmax(0,7fr)}
  main .lc-deck-sec .copy{position:static}
  main .lc-deck-sec .deck{position:relative;padding-bottom:0;
    height:calc(184px + (var(--figs,3) - 1) * 22px)}
  main .lc-deck-sec .fig{position:absolute;left:0;right:0;top:0;margin:0;
    will-change:transform}
  /* The cards are placed by transform, so the entrance latch must not also be
     transforming them — .lc-rise sets translateY(24px) and the deck would
     overwrite it mid-fade, which reads as a card twitching as it arrives.
     Opacity only here. */
  html.lcjs main .lc-deck-sec .fig.lc-rise{transform:none}
  @media (max-width:900px){
    main .lc-deck-sec{height:130vh}
    /* Same specificity trap as #record: `main .split{grid-template-columns:1fr}`
       is one class weaker than the rule above, so the tracks survive the
       breakpoint unless they are reset at the strength that set them. */
    /* THE GUTTER WAS MISSING ENTIRELY. `padding: ... 0 0` zeroed the sides, and
       .split is the section's own container — there is no .lc-in inside it to
       restore them — so on a phone the eyebrow, heading and lede of The Record
       sat hard against both screen edges while every other section on the site
       held 22px. Same clamp as .lc-in so it lines up with them exactly. */
    main .lc-deck-sec .split{grid-template-columns:1fr;align-items:start;gap:26px;
      height:100svh;padding:calc(var(--head-h) + 14px) clamp(18px,5vw,40px) 0}
    main .lc-deck-sec .copy{max-width:none}
    main .lc-deck-sec .deck{height:calc(150px + (var(--figs,3) - 1) * 22px)}
    main .lc-deck-sec .fig{padding:20px 24px 18px}
  }

  main .lc-figs{display:grid;gap:20px;margin-top:8px;
    grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
  main .lc-figs-1{grid-template-columns:minmax(0,460px)}
  main .lc-figs .fig{position:static;height:auto;margin:0;transform:none;
    will-change:auto;z-index:auto;padding:26px 30px 24px}

  /* ── the record, without the scrub ────────────────────────────────
     THREE OF THE FOUR FIGURES WERE UNREADABLE under reduced motion, on both the
     homepage and Projects. The deck places its cards by transform and script
     deals them apart on scroll; with no script they sit on one spot, so the top
     card covered the other three and the Record stated one number out of four.

     This one hid from every check I had. The cards are not hidden — all four
     are opacity 1, full size, inside the viewport, and a component count says
     4/4 present. They are OCCLUDED, which an opacity test cannot see. It took a
     screenshot to find, and the lesson is that "visible" in a probe means
     "painted", not "readable".

     .lc-figs already describes the static presentation this wants, so the deck
     borrows it rather than inventing a second one. */
  html:not(.lcjs) main #record .deck,
  html:not(.lcjs) main .lc-deck-sec .deck{height:auto;padding-bottom:0;
    display:grid;gap:16px}
  html:not(.lcjs) main #record .fig,
  html:not(.lcjs) main .lc-deck-sec .fig{position:static;height:auto;margin:0;
    transform:none;will-change:auto;z-index:auto}
  /* THE SECTION AND ITS PIN HAVE TO COME DOWN WITH THE DECK. Both are sized for
     a collapsed pile: 240vh of scroll for the cards to deal through, and a
     sticky pin fixed at one viewport tall. Releasing only the cards grew a
     688px deck inside a 100svh pin, so the last figure ran out under the
     section below it and the map's dark band painted over "Nationwide".
     Caught in a screenshot, again — the geometry checks all passed. */
  html:not(.lcjs) main #record,
  html:not(.lcjs) main .lc-deck-sec{height:auto;
    padding-top:var(--sp-sec);padding-bottom:var(--sp-sec)}
  html:not(.lcjs) main #record .split,
  html:not(.lcjs) main .lc-deck-sec .split{position:static;height:auto;
    align-items:start;padding-bottom:0}
  html:not(.lcjs) main #record .fig{margin-bottom:0}

  /* ── the coverage map ─────────────────────────────────────────────
     THE PANEL IS GONE (Katie, 13 Aug 2026: "make the entire background dark
     navy so the map blends seamlessly into the page rather than appearing as a
     rectangle"). It was a Prussian plate with a border, a radius and a drop
     shadow, sitting on the Imperial ground the page has reached by then — a
     visibly darker rectangle floating on navy, which is exactly what she saw.

     The dark now belongs to the SECTION and fades in and out of the ground at
     top and bottom, so there is no edge to read as a box. The transparent stops
     matter: whatever colour the ground journey is passing through shows through
     them, so the band dissolves into the page instead of butting against it.

     NO vw HERE EITHER — .lc-sec is already full width, so the background is
     full-bleed without any negative-margin arithmetic. See the bleed note above
     for why 100vw is banned.

     BOTH SIDES OF EVERY PAIR ARE PINNED. The section is dark whatever the
     ground is doing underneath it, so --fg and --now are set together; pinning
     one and inheriting the other is what put navy type on navy in the inquiry
     form and 1.00:1 in the nav. */
  main .lc-mapsec{
    --now:var(--prussian);
    --fg:var(--porcelain);
    --fg-dim:rgba(247,247,243,.74);
    --fg-accent:var(--mint);
    --edge:rgba(247,247,243,.20);
    --panel:#0a1c3c;
    color:var(--fg);
    background:linear-gradient(to bottom,
      rgba(3,15,38,0) 0,
      var(--prussian) clamp(60px,9vh,120px),
      var(--prussian) calc(100% - clamp(60px,9vh,120px)),
      rgba(3,15,38,0) 100%)}
  /* ── AND THE SECTION BELOW IT IS NOW DARK TOO (Katie, 14 Aug 2026) ──
     "Update background color in the 'What that work looks like' section to the
     same navy color as the Project Map." Same six declarations as above, for
     the same reason — the section is dark whatever the ground is doing, so
     both sides of every pair are pinned rather than half of them inherited.

     THE TWO BANDS ARE JOINED, NOT STACKED. The map fades out at its bottom
     edge and this one would fade in at its top, which on a light ground puts a
     bright seam between two dark sections. Where they are adjacent the map
     holds its colour to the bottom and this one starts at full strength, so
     the pair reads as one plate; only the outer two edges dissolve. :has() is
     the test and a browser without it simply gets the old pair of fades. */
  main .lc-cyc--dark{
    --now:var(--prussian);
    --fg:var(--porcelain);
    --fg-dim:rgba(247,247,243,.74);
    --fg-accent:var(--mint);
    --edge:rgba(247,247,243,.20);
    --panel:#0a1c3c;
    color:var(--fg);
    background:linear-gradient(to bottom,
      rgba(3,15,38,0) 0,
      var(--prussian) clamp(60px,9vh,120px),
      var(--prussian) calc(100% - clamp(60px,9vh,120px)),
      rgba(3,15,38,0) 100%)}
  main .lc-mapsec:has(+ .lc-cyc--dark){
    background:linear-gradient(to bottom,
      rgba(3,15,38,0) 0,
      var(--prussian) clamp(60px,9vh,120px),
      var(--prussian) 100%)}
  main .lc-mapsec + .lc-cyc--dark{
    background:linear-gradient(to bottom,
      var(--prussian) 0,
      var(--prussian) calc(100% - clamp(60px,9vh,120px)),
      rgba(3,15,38,0) 100%)}
  /* ── THE SLIDE-UP CARD (client, 24 Aug 2026) ────────────────────────
     Replaces the blend. The plate now rides up over the pinned Record as a
     panel with a hard top edge and a corner radius, so the change of ground is
     an object arriving rather than a colour dissolving.

     The movement needs no script: .lc-deck-sec's .split is already sticky, so
     this section travels up over held content on its own. What was hiding it
     was the 81px transparent-to-navy fade at this edge and the ground going
     dark behind it at the same time — together they erased the boundary the
     card depends on. Solid from the first pixel, and the ground stays light so
     there is something for the card to cover and cast onto.

     Both selectors carry the :has() form because the joined-plate rule above
     matches at (0,2,1) and would otherwise keep restoring its gradient. */
  main .lc-mapsec--card,
  main .lc-mapsec--card:has(+ .lc-cyc--dark){
    position:relative;z-index:3;
    background:var(--prussian);
    border-radius:clamp(16px,2.4vw,32px) clamp(16px,2.4vw,32px) 0 0;
    box-shadow:0 -16px 44px rgba(3,15,38,.30)}
  /* The card ends where the plate does, so the pair gets one radius top and
     bottom rather than the map opening as a card and the gallery dissolving. */
  main .lc-mapsec--card + .lc-cyc--dark{
    position:relative;z-index:3;
    background:var(--prussian);
    border-radius:0 0 clamp(16px,2.4vw,32px) clamp(16px,2.4vw,32px);
    box-shadow:0 16px 44px rgba(3,15,38,.30)}
  @media (prefers-reduced-motion:reduce){
    main .lc-mapsec--card{box-shadow:none}
  }
  main .lc-mapsec .lc-map{position:relative;margin-top:22px;overflow:hidden;
    min-height:clamp(260px,44vw,560px)}
  /* height:auto on a 975x610 viewBox, so Alaska and Hawaii — which sit in the
     bottom-left of the Albers frame — are inside the box rather than cropped
     by a fixed height. min-height above only holds the frame open before the
     geometry arrives. */
  main .lc-mapsec .lc-map-svg{display:block;width:100%;height:auto}
  main .lc-mapsec .lc-state{transition:fill var(--mo-ui) ease,opacity var(--mo-ui) ease}
  main .lc-mapsec .lc-map-svg:hover .lc-state{opacity:.72}
  main .lc-mapsec .lc-map-svg .lc-state:hover{opacity:1}
  main .lc-mapsec .lc-hq-halo{fill:rgba(0,161,175,.28)}
  main .lc-mapsec .lc-hq-dot{fill:var(--mint)}

  /* Follows the pointer, so it is positioned from JS and only styled here.
     translate(-50%,-140%) puts it above the cursor and clear of it; without
     the lift the tooltip sits under the pointer and flickers as the pointer
     enters its own box. pointer-events:none is what actually guarantees that. */
  main .lc-mapsec .lc-map-tip{position:absolute;left:0;top:0;pointer-events:none;
    transform:translate(-50%,-140%);opacity:0;transition:opacity var(--mo-ui) ease;
    background:rgba(3,15,38,.94);border:1px solid rgba(247,247,243,.16);
    border-radius:var(--r-sm);padding:8px 12px;white-space:nowrap;z-index:2}
  main .lc-mapsec .lc-map-tip.is-on{opacity:1}
  main .lc-mapsec .lc-map-tip strong{display:block;font-size:13.5px;color:var(--porcelain)}
  main .lc-mapsec .lc-map-tip span{display:block;font-size:12px;color:rgba(247,247,243,.66)}

  main .lc-mapsec .lc-map-chips{display:flex;flex-wrap:wrap;gap:10px;margin-top:18px}
  main .lc-mapsec .lc-chip{font:inherit;font-size:13px;font-weight:700;cursor:pointer;
    padding:9px 16px;border-radius:var(--r-pill);background:transparent;
    border:1px solid var(--edge);color:var(--fg-dim);
    transition:background var(--mo-ui) ease,color var(--mo-ui) ease,border-color var(--mo-ui) ease}
  main .lc-mapsec .lc-chip:hover{border-color:var(--fg-accent);color:var(--fg)}
  main .lc-mapsec .lc-chip.is-on{background:var(--fg);color:var(--now);border-color:var(--fg)}

  /* 13px -> 15px, at the client's request. The old key was the smallest type on
     the site and it is the one thing on this section a reader has to read to
     understand the picture. */
  main .lc-mapsec .lc-map-key{display:flex;flex-wrap:wrap;gap:18px 34px;margin-top:22px;
    align-items:flex-end;font-size:15px;color:var(--fg-dim)}
  main .lc-mapsec .lc-key{display:inline-flex;align-items:center;gap:10px}
  main .lc-mapsec .lc-key i{display:block;border-radius:var(--r-xs)}
  /* The scale key: a caption, the ramp, and the numbers under it. The two ramp
     colours are the ends of RAMP in lc-map.js and the midpoint is its middle
     stop — three values in two files that have to stay in step, which is why
     both carry the note. */
  main .lc-mapsec .lc-key--scale{flex-direction:column;align-items:flex-start;gap:7px}
  main .lc-mapsec .lc-key-cap{font-size:13px;letter-spacing:.06em;
    text-transform:uppercase;color:var(--fg-accent)}
  main .lc-mapsec .lc-key-bar{display:block;width:min(240px,52vw)}
  main .lc-mapsec .lc-key-ramp{width:100%;height:11px;border-radius:var(--r-pill);
    background:linear-gradient(90deg,rgb(12,41,71),rgb(0,118,132),rgb(127,208,215));
    border:1px solid rgba(247,247,243,.18)}
  main .lc-mapsec .lc-key-ticks{display:flex;justify-content:space-between;
    margin-top:5px;font-size:13px;font-variant-numeric:tabular-nums;
    color:var(--fg-dim)}
  main .lc-mapsec .lc-key-hq{width:10px;height:10px;border-radius:var(--r-pill);background:var(--mint);
    box-shadow:0 0 0 4px rgba(0,161,175,.28)}

  /* Hover states are a pointer idea. On touch the whole-map dim would latch on
     first tap and never clear, leaving the map permanently greyed. */
  @media (hover:none){
    main .lc-mapsec .lc-map-svg:hover .lc-state{opacity:1}
    main .lc-mapsec .lc-map-tip{display:none}
  }

  /* ── cards ────────────────────────────────────────────────────── */
  /* ── centred block head ───────────────────────────────────────────
     Used by the core-values block. The eyebrow, heading and standfirst all
     take their measures from the left-aligned default, so centring is not just
     text-align — the max-widths have to be re-centred with auto margins or the
     block reads as centred text hanging off a left-aligned column. */
  main .lc-head-c{text-align:center;max-width:760px;margin:0 auto}
  main .lc-head-c h2{margin-left:auto;margin-right:auto}
  main .lc-head-c .lede{margin-left:auto;margin-right:auto;max-width:64ch}
  main .lc-head-c .eyebrow{max-width:none}

  /* ── the core-values accordion ────────────────────────────────────
     One open photographic panel, the rest collapsed to slivers. The open state
     is a class and every movement here is a transition on it — lc-sections.js
     only decides which panel carries it.

     flex-grow IS THE ANIMATION. Transitioning grow rather than width keeps the
     row exactly full at every frame of the move: the panels always sum to the
     container, so there is no sub-pixel gap opening and closing at the right
     edge as they swap, which is what animating widths against a fixed
     container gives you. */
  main .lc-acc{display:flex;gap:10px;margin-top:clamp(30px,4vh,48px);
    height:clamp(400px,56vh,600px)}
  main .lc-acc-p{position:relative;flex:1 1 0;min-width:0;
    display:block;padding:0;border:0;cursor:pointer;text-align:left;
    border-radius:var(--r-lg);overflow:hidden;color:var(--porcelain);
    background:var(--imperial) var(--acc-img,none) center/cover no-repeat;
    transition:flex-grow var(--mo-slow) cubic-bezier(.22,1,.36,1)}
  main .lc-acc-p.is-open{flex-grow:5.2;cursor:default}
  main .lc-acc-p:focus-visible{outline:2px solid var(--pacific);outline-offset:3px}
  /* Heavier on the closed panels: a sliver is mostly texture and wants to read
     as brand colour, while the open one has to show its photograph. */
  main .lc-acc-veil{position:absolute;inset:0;pointer-events:none;
    background:linear-gradient(180deg,rgba(3,15,38,.30),rgba(3,15,38,.86));
    opacity:.92;transition:opacity var(--mo-move) ease}
  /* The open panel's ramp starts earlier and lands harder than it looks like
     it needs to. Measured on the render, the first version cleared the title
     and left the COPY sitting on hi-vis vests and sky at roughly 2.6:1 — a
     photograph is only a background where it is dark, and this one is bright
     exactly where the paragraph is. The image still reads through the top
     two-fifths, which is the part doing the work. */
  main .lc-acc-p.is-open .lc-acc-veil{opacity:.78;
    background:linear-gradient(180deg,rgba(3,15,38,.06) 18%,
      rgba(3,15,38,.62) 48%,rgba(3,15,38,.95))}
  /* The numeral is the sliver's whole label, so it stays centred while closed
     and steps aside to the copy's own left edge once the panel opens. */
  main .lc-acc-num{position:absolute;top:22px;left:0;right:0;
    text-align:center;font-size:15px;font-weight:700;letter-spacing:.16em;
    color:rgba(247,247,243,.72);
    transition:transform var(--mo-slow) cubic-bezier(.22,1,.36,1),color var(--mo-move) ease}
  main .lc-acc-p.is-open .lc-acc-num{color:var(--pacific);
    transform:translateX(calc(-50% + clamp(26px,2.6vw,40px) + 1.2em))}
  main .lc-acc-body{position:absolute;left:0;right:0;bottom:0;
    padding:clamp(24px,2.8vw,40px);
    opacity:0;transform:translateY(14px);pointer-events:none;
    transition:opacity var(--mo-move) ease,transform var(--mo-move) cubic-bezier(.22,1,.36,1)}
  main .lc-acc-p.is-open .lc-acc-body{opacity:1;transform:none;transition-delay:.16s}
  main .lc-acc-t{display:block;font-size:clamp(20px,2.1vw,28px);font-weight:700;
    letter-spacing:-.02em;line-height:1.12;color:var(--porcelain)}
  main .lc-acc-c{display:block;margin-top:12px;max-width:52ch;
    font-size:14.5px;line-height:1.6;color:rgba(247,247,243,.82)}

  /* Column at narrow: the same mechanism turned ninety degrees, so one panel
     is open and the others are bands. A grid of five photographic cards would
     lose the one-thing-at-a-time reading the pattern exists for. */
  @media (max-width:900px){
    main .lc-acc{flex-direction:column;height:auto;gap:8px}
    main .lc-acc-p{flex:0 0 auto;height:76px;
      transition:height var(--mo-slow) cubic-bezier(.22,1,.36,1)}
    main .lc-acc-p.is-open{flex:0 0 auto;height:clamp(330px,58vh,460px)}
    main .lc-acc-num{top:0;line-height:76px;left:20px;right:auto;text-align:left}
    main .lc-acc-p.is-open .lc-acc-num{transform:none}
    main .lc-acc-t{padding-left:2.4em}
    main .lc-acc-p.is-open .lc-acc-t{padding-left:0}
  }
  /* Reduced motion: the row still works, it just arrives instead of moving. */
  @media (prefers-reduced-motion:reduce){
    main .lc-acc-p,main .lc-acc-body,main .lc-acc-num,main .lc-acc-veil{transition:none}
  }

  main .lc-cards{display:grid;gap:18px;margin-top:8px;
    grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
  main .lc-card{background:var(--panel);border:1px solid var(--edge);border-radius:var(--r-lg);
    padding:26px 28px 28px;transition:background var(--mo-ui) ease,border-color var(--mo-ui) ease,transform var(--mo-move) cubic-bezier(.22,1,.36,1)}
  main .lc-card p{margin:0;font-size:15px;line-height:1.6;color:var(--fg-dim);
    max-width:none;transition:color var(--mo-ui) ease}

  /* ── grouped lists ────────────────────────────────────────────────
     The green leading edge is the same seam the mobile drawer uses. Spent here
     it marks structure rather than decorating, which is the only reason to
     keep it anywhere. */
  /* THREE COLUMNS, NOT auto-fit's TWO (audit, 13 Aug 2026). At the 1007px this
     block actually gets, minmax(300px) plus a 56px gap needs 1012 to fit three
     tracks — five pixels short. So it resolved to two, and with three groups
     the third wrapped: Finance sat alone on the left under a 266px column with
     the entire right half of the row empty, which reads as content that failed
     to load rather than as a department with one opening.
     Fixed at three so the row is always complete. The same fault as the market
     grid and the form: auto-fit decides the column count from arithmetic
     nobody checked, and is silently wrong the moment the container changes. */
  /* 8px under the intro paragraph put the first job heading effectively on top
     of it — measured, the gap was -10px once the paragraph's descender space is
     counted. The rest of the site changes subject on 34-46px. */
  /* --cols and --cols-m come from the block, capped at 3 and 2 — see the note
     at louth_callan_block_list(). A one-group list is now one full-width
     column instead of a third of the row with everything in it wrapping. */
  main .lc-groups{display:grid;gap:clamp(24px,3vw,44px);margin-top:clamp(34px,4.2vw,54px);
    grid-template-columns:repeat(var(--cols,3),minmax(0,1fr));align-items:start}
  /* 19px over 16.5px body is 2.5px of separation — the heading and the job
     titles under it were nearly the same size, so the grouping did not read. */
  main .lc-group h3{margin:0 0 14px;font-size:24px;font-weight:700;
    letter-spacing:-.02em;line-height:1.2;color:var(--fg);
    transition:color var(--mo-ui) ease}
  main .lc-group ul{margin:0;padding:0;list-style:none}
  main .lc-group li{position:relative;margin:0 0 12px;padding:0 0 12px 18px;
    font-size:15.5px;line-height:1.55;color:var(--fg-dim);
    border-bottom:1px solid var(--edge);transition:color var(--mo-ui) ease,border-color var(--mo-ui) ease}
  main .lc-group li:last-child{border-bottom:0;margin-bottom:0}
  @media (max-width:900px){
    main .lc-groups{grid-template-columns:repeat(var(--cols-m,2),minmax(0,1fr))}
  }
  @media (max-width:600px){
    main .lc-groups{grid-template-columns:minmax(0,1fr)}
  }
  main .lc-group li::before{content:'';position:absolute;left:0;top:.55em;
    width:6px;height:6px;border-radius:50%;background:var(--green)}
  /* Each role now opens its Paylocity application. The link fills the row so
     the whole line is the target rather than the words alone — these are read
     on a phone, and a 15.5px text hit area is not a tap target. The arrow is
     the only affordance added; the row keeps the body colour until hover, so
     fourteen links do not turn the section into a wall of accent. */
  main .lc-group li a{display:flex;align-items:baseline;gap:8px;
    color:inherit;text-decoration:none;
    transition:color var(--mo-ui) ease}
  /* The anchor's own box is 24px tall — the line box of one 15.5px title — and
     that is not a tap target. This grows the HIT AREA to 44px across the whole
     row, bullet included, without moving anything: the overlay is measured off
     the li, and the 12px margin between rows leaves 4px of clear space so two
     targets never touch. */
  main .lc-group li a::after{content:'';position:absolute;
    inset:-10px -4px 2px -18px}
  main .lc-group li a:hover,
  main .lc-group li a:focus-visible{color:var(--fg-accent)}
  main .lc-job-out{flex:none;width:11px;height:11px;opacity:0;translate:-3px 0;
    transition:opacity var(--mo-ui) ease,translate var(--mo-ui) ease}
  main .lc-job-out svg{width:100%;height:100%;
    stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
  main .lc-group li a:hover .lc-job-out,
  main .lc-group li a:focus-visible .lc-job-out{opacity:1;translate:0 0}
  @media (prefers-reduced-motion:reduce){
    main .lc-job-out{transition:opacity var(--mo-ui) ease;translate:none}
  }
  /* Kept off-screen rather than display:none — display:none is not announced. */
  .lc-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
    overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}

  /* ── split ────────────────────────────────────────────────────── */
  main .lc-split{display:grid;grid-template-columns:minmax(0,6fr) minmax(0,5fr);
    gap:clamp(32px,5vw,72px);align-items:center}
  /* Picture on the left. Ordered rather than reordered in the markup — the
     source order is the reading order, so copy stays first in the DOM and a
     screen reader still gets the heading before the plate that illustrates it.
     The tracks swap too, because 6fr/5fr is a copy-major split and flipping
     only the children would leave the picture in the wide column. */
  /* EQUAL COLUMNS, AND THE PLATE TAKES THE COPY'S HEIGHT (Katie, 13 Aug 2026:
     "can the left-aligned image below the hero be larger to match the size of
     the copy?"). It was the smaller half of a 5fr/6fr split and locked to 4/3,
     so it finished 170px short of the paragraphs beside it.

     align-self rather than a stretch on the whole grid: .lc-split centres both
     columns and the copy should stay centred if it is ever the shorter one.
     height:100% with no aspect-ratio, NOT aspect-ratio:auto — a grid child with
     an aspect ratio and no definite inline size is the bug that collapsed five
     mastheads to 0px wide in WebKit, and there is no reason to go near it when
     the row height is already definite. */
  main .lc-split--flip{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  main .lc-split--flip .lc-split-media{order:-1;align-self:stretch;display:flex}
  main .lc-split--flip .lc-frame{aspect-ratio:auto;height:100%;width:100%;
    min-height:clamp(260px,26vw,420px)}

  /* ── the bleeding plate ───────────────────────────────────────────
     THE HOMEPAGE'S CLOSING TREATMENT, MIRRORED. #leadership runs its photograph
     off the RIGHT edge with the left corners rounded; this runs off the LEFT
     with the right corners rounded, because the media sits on the other side.

     The arithmetic is the same and so is the reason for it: the container gives
     up its max-width and its left padding entirely, and the single padding-RIGHT
     carries both the centring and the gutter. With nothing holding the left
     side, the plate ends flush with the window.

     NO vw, for the reason given at the top of #leadership — 100vw counts the
     classic scrollbar, overhangs by ~15px on Windows, and the resulting
     horizontal scrollbar makes 100vw larger again. The percentage resolves
     against the container, which is the real client width, and cannot overhang.

     1180 not 1100: .lc-in caps at 1180 where .lead-pin caps at 1100. Using the
     homepage's number here would have put the copy 40px off centre.

     TRACK ORDER IS REVERSED, NOT THE COLUMNS. .lc-split-media carries order:-1,
     so it is placed into the FIRST track — which is why the wide track has to be
     first. Writing 4fr/7fr as the homepage does would have given the photograph
     the narrow column and the copy the wide one. */
  main .lc-split--flip.lc-split--bleed{max-width:none;
    grid-template-columns:minmax(0,7fr) minmax(0,4fr);
    align-items:center;
    padding:0 calc(max(0px, (100% - 1180px) / 2) + clamp(24px,6vw,96px)) 0 0}
  main .lc-split--flip.lc-split--bleed .lc-split-media{align-self:center;display:block}
  /* 3/2 matches the homepage plate. Corners on the inner edge only: a rounded
     corner against the window edge is the tell that something is a box near the
     edge rather than something running off it. The shadow goes with them — it
     was casting onto a gutter that no longer exists. */
  main .lc-split--flip.lc-split--bleed .lc-frame{aspect-ratio:3/2;height:auto;
    min-height:0;border:0;box-shadow:none;
    border-radius:0 var(--r-md) var(--r-md) 0}
  main .lc-split-copy p{margin:0 0 16px;max-width:52ch;font-size:16.5px;line-height:var(--lh-body);
    color:var(--fg-dim);transition:color var(--mo-ui) ease}
  main .lc-split-copy p:last-child{margin-bottom:0}
  main .lc-frame{aspect-ratio:4/3;border-radius:var(--r-lg);background-size:cover;
    background-position:center;box-shadow:0 26px 60px rgba(3,15,38,.22)}
  main .lc-split-media .ph{aspect-ratio:4/3;border-radius:var(--r-lg);
    background:#E9EBEE;border:1px solid rgba(10,27,63,.10);
    display:flex;align-items:center;justify-content:center}
  main .lc-split-media .ph svg{width:clamp(42px,6vw,64px);height:auto;fill:none;
    stroke:#A6ADB8;stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round}
  main .lc-split-media .ph svg circle{fill:#A6ADB8;stroke:none}
  @media (max-width:900px){
    main .lc-split{grid-template-columns:1fr;gap:30px}
    /* Media above copy for EVERY split at this width, flipped or not — which
       is why .lc-split--flip needs no narrow case of its own. */
    main .lc-split-media{order:-1}
    /* THE BLEED DOES NEED ONE. One column means there is no second column to
       bleed into, so the plate goes back to being inset on both sides and takes
       all four corners again — the same call #leadership makes at this width.
       BOTH DECLARATIONS HAVE TO BE REPEATED, not just overridden: the desktop
       rule is two classes deep and a media query adds no specificity, so a
       single-class reset inside here would lose to it. That is the bug that
       kept #leadership two columns down to 390px. */
    main .lc-split--flip.lc-split--bleed{grid-template-columns:1fr;
      max-width:1180px;padding:0 clamp(18px,5vw,40px)}
    main .lc-split--flip.lc-split--bleed .lc-frame{border-radius:var(--r-lg)}
  }

  /* ── people ───────────────────────────────────────────────────── */
  main .lc-people{display:grid;gap:20px;margin-top:8px;
    grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
  main .lc-person{background:var(--panel);border:1px solid var(--edge);border-radius:var(--r-lg);
    padding:24px 26px 26px;transition:background var(--mo-ui) ease,border-color var(--mo-ui) ease}
  main .lc-person h3{margin:0 0 4px;font-size:18.5px}
  main .lc-role{margin:0;font-size:14px;line-height:1.45;color:var(--fg-accent);
    font-weight:600;transition:color var(--mo-ui) ease}
  main .lc-bio{margin:12px 0 0;font-size:14.5px;line-height:1.6;color:var(--fg-dim)}
  /* Stands where a portrait will, exactly as Leadership's .ph does. Swap for
     an <img> or a background-image and nothing else moves. */
  main .lc-portrait{width:56px;height:56px;border-radius:50%;margin:0 0 16px;
    background:rgba(127,208,215,.14);border:1px solid var(--edge);
    display:flex;align-items:center;justify-content:center}
  main .lc-portrait svg{width:26px;height:26px;fill:none;stroke:var(--fg-accent);
    stroke-width:1.5;stroke-linecap:round}

  /* ── people, collage style ────────────────────────────────────────
     A mosaic of eleven plates on a 12-column grid, hand-placed so it tiles
     EXACTLY — every cell of the 12x13 field is covered and the block has four
     straight edges. That is the reason for explicit placement rather than a
     repeating span cycle with grid-auto-flow:dense, which was tried first and
     measured 26.5% empty with a plate-sized hole down the right-hand side.
     Dense flow can only back-fill a gap when a later item happens to fit it;
     with mixed 3/4/5-column widths most gaps have no candidate.

     Sizes are deliberately unequal — one tall 4:5 lead, one large square, and
     a mix of near-square and portrait — so it reads as a collage.

     ADDING A TWELFTH PERSON is safe: placement is by nth-child, so anyone
     past the eleventh auto-flows underneath at the default 4x4 span. It will
     sit below the mosaic rather than inside it, which is the honest failure —
     re-tiling for a new count is a deliberate edit, not something to leave to
     chance. The row unit is vw-based so the whole field scales.

     Reveal is .lc-rise plus a per-index delay; hover uses `scale` rather than
     `transform`, because .lc-rise.in sets transform:none and the two would
     overwrite each other. */
  /* ONE TILE SIZE, NOT A MOSAIC (12 Aug 2026). This was an eleven-piece collage
     of deliberately unequal tiles — a 5x5 lead, then 3x3s and 4x4s. As a
     composition it was the strongest thing on the page; as a way to show
     eleven portraits it worked against them twice.
     Every tile had a DIFFERENT ASPECT, so object-fit:cover re-cropped each
     face by a different amount. The portraits are now cut to one rule — face
     width 44% of frame, eyes on a common line — and a mosaic throws that away
     the moment it renders them at 5:5, 4:3 and 3:4.
     It also sized the tiles by composition rather than by what the photographs
     could carry: the 5x5 lead asked for 1034px of height from sources that top
     out at 800. Four equal 4:5 columns render at 264 CSS, which the good
     sources cover natively.
     `width:100%` is load-bearing — see the note on .lc-mast-media. */
  main .lc-team-collage{margin-top:clamp(26px,3.6vw,48px);display:grid;
    grid-template-columns:repeat(4,minmax(0,1fr));
    gap:clamp(10px,1vw,14px)}
  main .lc-plate{position:relative;margin:0;overflow:hidden;border-radius:var(--r-md);
    background:var(--panel);border:1px solid var(--edge);
    width:100%;aspect-ratio:4/5;
    transition:scale var(--mo-ui) cubic-bezier(.22,1,.36,1),box-shadow var(--mo-ui) cubic-bezier(.22,1,.36,1)}
  /* ONE TREATMENT ACROSS ELEVEN SOURCES (client, 12 Aug 2026). The portraits
     were supplied from eleven different places — measured, their saturation
     ran from 0.00 (one already black-and-white) to 0.56 (a red brick wall),
     and their average colour from a warm brown to a pale wall. No layout
     rescues a set that inconsistent; a single tonal range does, and it reads
     as one commissioned shoot rather than eleven snapshots.

     It also hides the resolution gap: seven of the eleven arrived under 400px
     and are upscaled, and sharpening artefacts are far less visible without
     colour to carry them.

     A FILTER RATHER THAN BAKED-IN, so the originals stay colour and this is one
     line to revert when a matching shoot exists. The contrast nudge is because
     a flat grayscale of mixed sources goes muddy in the midtones. */
  main .lc-plate img{display:block;width:100%;height:100%;object-fit:cover;
    filter:grayscale(1) contrast(1.06) brightness(1.02)}
  main .lc-plate-ph{position:absolute;inset:0;background:rgba(127,208,215,.10)}
  /* Cascade rather than one simultaneous arrival. */
  html.lcjs main .lc-plate.lc-rise{transition-delay:calc(min(var(--i,0),11) * 45ms)}
  main .lc-plate:hover,
  main .lc-plate:focus-within{scale:1.05;z-index:2;
    box-shadow:0 18px 44px rgba(3,15,38,.32)}
  main .lc-plate-cap{position:absolute;left:0;right:0;bottom:0;margin:0;
    padding:26px 14px 12px;
    background:linear-gradient(to top,rgba(3,15,38,.88),rgba(3,15,38,0));
    opacity:0;transform:translateY(8px);
    transition:opacity var(--mo-ui) ease,transform var(--mo-ui) cubic-bezier(.22,1,.36,1)}
  main .lc-plate:hover .lc-plate-cap,
  main .lc-plate:focus-within .lc-plate-cap{opacity:1;transform:none}
  main .lc-plate-cap b{display:block;font-size:14.5px;font-weight:700;
    letter-spacing:-.01em;line-height:1.2;color:#fff}
  main .lc-plate-cap span{display:block;margin-top:2px;font-size:12px;
    line-height:1.3;color:rgba(255,255,255,.78)}

  /* No pointer means no hover, so the names are permanent there. */
  @media (hover:none){
    main .lc-plate-cap{opacity:1;transform:none}
    main .lc-plate:hover{scale:1;box-shadow:none}
  }
  @media (max-width:1080px){
    main .lc-team-collage{grid-template-columns:repeat(3,minmax(0,1fr))}
  }
  @media (max-width:760px){
    main .lc-team-collage{grid-template-columns:repeat(2,1fr)}
  }

  /* ── the burst ────────────────────────────────────────────────────
     Six scope cards around a centred statement, dispersing outward as the
     section is scrolled. Script writes --p (0-1) on the stage; every card
     derives its own radius from it, so one property write moves all six.

     --r0 is in vmin so the cluster keeps its shape on any viewport shape;
     the travel is in vmax so a card always clears the longer edge. Neither
     may be a percentage — a percentage inside translateX() resolves against
     the CARD, not the stage. */
  /* ── the stacking deck ────────────────────────────────────────────
     Base state is a plain column of panels — a complete, readable index with
     no script and no motion. Everything below the gate turns that column into
     a stack; nothing about the content depends on it. */
  main .lc-stack-deck{display:grid;gap:clamp(18px,3vh,34px);
    max-width:1180px;margin:clamp(26px,4vw,44px) auto 0;
    padding:0 clamp(24px,6vw,96px)}
  main .lc-stack-card{display:block;text-decoration:none;position:relative}
  /* A CARD HAS TO BE A DIFFERENT COLOUR FROM THE PAGE (David, 12 Aug 2026:
     "this is still the card stacking animation" — it was not landing).
     The surface was var(--panel), which is derived from the ground and tracks
     it down the whole document. So every card was within a few percent of
     whatever was directly behind it: the pile had no edges, the shadows had
     nothing to fall on, and six planes stacking up read as one flat column of
     text. The motion was working the entire time; it had nothing to show.
     Pinned to Porcelain with the type tokens to match — the same fix the
     inquiry form needed, and for the same reason. A deeper shadow because it
     now has a surface to sit above. */
  main .lc-stack-inner{display:grid;grid-template-columns:1.02fr .98fr;
    min-height:clamp(300px,58vh,470px);border-radius:var(--r-lg);overflow:hidden;
    --fg:rgb(8,39,103);--fg-dim:rgb(70,70,63);--fg-accent:rgb(0,161,175);
    --edge:rgba(8,39,103,.14);
    background:var(--porcelain);border:1px solid rgba(8,39,103,.12);
    box-shadow:0 26px 64px rgba(3,15,38,.26);
    transition:border-color var(--mo-ui) ease,background var(--mo-ui) ease}
  main .lc-stack-copy{display:flex;flex-direction:column;align-items:flex-start;
    justify-content:center;gap:12px;padding:clamp(26px,3.4vw,52px)}
  main .lc-stack-num{font-size:11.5px;font-weight:700;letter-spacing:.18em;
    color:var(--fg-accent)}
  main .lc-stack-name{font-size:clamp(24px,2.8vw,40px);font-weight:700;
    letter-spacing:-.028em;line-height:1.06;color:var(--fg);max-width:16ch;
    transition:color var(--mo-ui) ease}
  main .lc-stack-blurb{font-size:clamp(14.5px,1.35vw,16.5px);line-height:1.6;
    color:var(--fg-dim);max-width:44ch;transition:color var(--mo-ui) ease}
  main .lc-stack-go{margin-top:6px;font-size:13px;font-weight:700;
    letter-spacing:.02em;color:var(--fg-accent)}
  main .lc-stack-go::after{content:'\2009\2192';
    transition:margin-left var(--mo-ui) cubic-bezier(.22,1,.36,1)}
  main .lc-stack-card:hover .lc-stack-go::after{margin-left:6px}
  main .lc-stack-media{background:var(--panel) center/cover no-repeat;min-height:200px}

  /* THE STACK. Every card sticks at the same offset, so each one comes to rest
     exactly where the last did and the next rides up over it. --d is how far
     this card has been covered by the one after it, written by lc-sections.js;
     scale and dim both hang off it so a covered card recedes instead of simply
     being hidden. transform-origin at the top edge is what makes it read as
     going BACK into a pile rather than shrinking toward its own middle. */
  html.lcjs main .lc-stack-deck{perspective:1500px}
  /* EACH CARD RESTS A LIP LOWER THAN THE ONE BEFORE IT. With a common offset
     the covered card is hidden outright and the section reads as a slideshow —
     one card replacing another — rather than as a pile. 14px per card is
     enough to show an edge and a shadow without stealing height from the card
     you are actually reading; six cards spend 70px of the viewport on it. */
  html.lcjs main .lc-stack-card{position:sticky;
    top:calc(var(--head-h,68px) + clamp(12px,2.6vh,30px) + var(--i,0) * 14px);
    transform-origin:50% 0;
    transform:translateY(calc(var(--d,0) * -8px)) scale(calc(1 - var(--d,0) * .07));
    will-change:transform}
  /* The dim is a plate over the card rather than a filter on it: a filter on a
     sticky element promotes every card to its own layer and costs more than
     the effect is worth on a six-card deck. */
  html.lcjs main .lc-stack-card::after{content:'';position:absolute;inset:0;
    border-radius:var(--r-lg);pointer-events:none;background:var(--now);
    opacity:calc(var(--d,0) * .42)}
  @media (max-width:820px){
    main .lc-stack-inner{grid-template-columns:1fr;min-height:0}
    main .lc-stack-media{min-height:clamp(190px,34vh,260px);order:-1}
    /* A stack needs a card shorter than the viewport to stack INTO. On a phone
       the card is most of the screen, so the pile has nowhere to go and the
       effect becomes a stutter — the column is better. */
    html.lcjs main .lc-stack-card{position:static;transform:none;will-change:auto}
    html.lcjs main .lc-stack-card::after{display:none}
  }

  main .lc-sec.lc-burst{padding:0}
  main .lc-burst > .lc-burst-track{max-width:none;margin:0}
  /* THREE ACROSS, NOT auto-fit. auto-fit resolved to five columns at 1440 and
     left the sixth market alone on a second row — six items want 3x2 or 2x3,
     never 5+1. Explicit columns also mean the composition is the same shape on
     every desktop width instead of re-flowing between four and five. */
  main .lc-burst-stage{display:grid;gap:clamp(20px,2.6vw,34px) clamp(16px,2.4vw,26px);
    grid-template-columns:repeat(3,minmax(0,1fr));
    padding:clamp(28px,5vw,64px) clamp(20px,5vw,64px)}
  @media (max-width:900px){
    main .lc-burst-stage{grid-template-columns:repeat(2,minmax(0,1fr))}
  }
  @media (max-width:560px){
    main .lc-burst-stage{grid-template-columns:minmax(0,1fr)}
  }
  main .lc-burst-copy{grid-column:1/-1;text-align:center;max-width:62ch;margin:0 auto}
  /* The eyebrow inherits a ch-based max-width from the shared measure, so
     text-align alone leaves its BOX ranged left inside the centred column —
     measured 429px wide against a 560px column, sitting 65px off centre. */
  main .lc-burst-copy .eyebrow{margin:0 auto 12px;max-width:none}
  main .lc-burst-copy h2{margin:0 auto 14px;max-width:20ch}
  main .lc-burst-copy .lede{margin:0 auto;max-width:64ch;color:var(--fg-dim);
    font-size:var(--t-lede);line-height:1.6;transition:color var(--mo-ui) ease}
  main .lc-burst-card{display:block;text-decoration:none}
  main .lc-burst-img{display:block;width:100%;aspect-ratio:4/3;border-radius:var(--r-md);
    background:var(--panel) center/cover no-repeat;border:1px solid var(--edge);
    box-shadow:0 14px 40px rgba(3,15,38,.26)}
  main .lc-burst-label{display:block;margin-top:10px;font-size:13.5px;font-weight:700;
    line-height:1.25;color:var(--fg);transition:color var(--mo-ui) ease}
  /* What the market actually is. The section had six titles and no sentence
     about any of them, which is the other half of why it read as decoration
     rather than as an index — there was nothing to land on. Read from the same
     registry blurb the homepage rail and the market pages use. */
  main .lc-burst-blurb{display:block;margin-top:6px;font-size:13px;line-height:1.5;
    color:var(--fg-dim);transition:color var(--mo-ui) ease}

  /* The scrubbed state. Gated on lcjs so a no-JS page keeps the grid above —
     these cards are the only route to the six scope pages and must never
     need a scroll handler to be reachable. */
  /* THE TRAVEL RUNS INWARD (client, 12 Aug 2026: "I'm not sure it is landing
     as strongly as intended"). It used to run outward — six cards dispersing
     from a centred statement to the edges of the screen. The motion was fine;
     the direction was wrong. Those six cards are the ONLY route to the six
     market pages, so an index that travels away from the reader as they scroll
     is working against the one job the page has.

     Reversed, the same vocabulary reads as an assembly: the markets arrive
     from off-stage and settle into a grid you can read and click, with the
     statement above them rather than buried underneath. Nothing about the
     resting state is scripted — the grid below IS the layout, and the scrub
     only offsets cards away from where they already belong, so the further
     the reader scrolls the closer the page gets to its own base state. */
  /* NO PIN. The outward version needed 190vh of track to scrub against while
     the stage was held; converging into a readable grid does not, and holding
     it made things worse — six cards at three across plus the statement is
     1281px against a 900px viewport, so the stage was taller than the screen,
     sticky had nothing to pin against, and the heading scrolled away from the
     index it introduces. The section is now an ordinary block and the assembly
     plays as it comes into view, which is where a reader is looking anyway. */
  html.lcjs main .lc-burst-track{position:relative}
  html.lcjs main .lc-burst-stage{max-width:1180px;margin:0 auto;overflow:hidden}
  html.lcjs main .lc-burst-copy{margin-bottom:clamp(22px,3.4vw,44px)}
  html.lcjs main .lc-burst-card{
    /* Where the card comes IN from, along its own angle. A single dispersion
       distance is right now that the cards no longer have to stop precisely at
       the viewport edge — they only have to start off it, and vmin keeps that
       true on any viewport shape. */
    --rIn:clamp(300px,52vmin,660px);
    --r:calc(var(--rIn) * (1 - var(--p,0)));
    opacity:calc(.06 + .94 * var(--p,0));
    transform:rotate(var(--a)) translateX(var(--r)) rotate(calc(-1 * var(--a)));
    will-change:transform,opacity}
  html.lcjs main .lc-burst-card:hover .lc-burst-img{box-shadow:0 20px 52px rgba(3,15,38,.42)}
  /* The blurb only exists once the card has somewhere to sit still and be
     read — it fades in over the last third, after the travel has mostly run. */
  html.lcjs main .lc-burst-blurb{opacity:calc((var(--p,0) - .62) * 2.7)}

  /* A radial dispersal needs room to disperse into. At 390px the copy fills
     most of the stage and every card overflowed the viewport by 30-100px, so
     below this the scrub is dropped entirely and the same six links render as
     the ordinary grid — the identical fallback a no-JS page gets. */
  @media (max-width:760px){
    html.lcjs main .lc-burst-track{position:static;height:auto}
    html.lcjs main .lc-burst-stage{position:static;height:auto;display:grid;
      overflow:visible;padding:clamp(28px,5vw,64px) clamp(20px,5vw,64px)}
    html.lcjs main .lc-burst-copy{position:static;width:auto;transform:none;
      pointer-events:auto;grid-column:1/-1}
    html.lcjs main .lc-burst-card{position:static;width:auto;transform:none;
      opacity:1;will-change:auto}
  }

  /* ── the cycle ────────────────────────────────────────────────────
     Full-bleed pairs advancing on scroll. Panel opacity and the parallax
     offset are set by script; everything else is here. */
  /* NO overflow:hidden ON THIS SECTION. An ancestor with a clipping overflow
     becomes the sticky element's scroll container, and the stage stops
     sticking — measured: it scrolled away leaving a navy viewport. The stage
     itself clips, which is where the clipping was actually needed. */
  main .lc-sec.lc-cyc{overflow:visible}
  main .lc-cyc > .lc-cyc-track{max-width:none;margin:clamp(24px,4vw,44px) 0 0}
  main .lc-cyc-stage{position:relative;width:100%;height:clamp(420px,74vh,760px);
    overflow:hidden;background:var(--stage)}
  main .lc-cyc-panel{position:absolute;inset:0;display:grid;
    grid-template-columns:1fr 1fr;gap:2px}
  main .lc-cyc-half{display:block;overflow:hidden}
  main .lc-cyc-half img{display:block;width:100%;height:100%;object-fit:cover;
    transform:translate3d(0,var(--dy,0px),0)}
  main .lc-cyc-half:nth-child(2) img{--dy:calc(var(--dy2,0px))}
  /* The panels are stacked and all present, so only the current one may take a
     click — otherwise the topmost swallows every one through the five beneath.
     lc-sections.js marks it; without script the panels are a vertical column
     and every link is live. */
  main .lc-cyc-link{position:absolute;inset:0;z-index:3}
  /* The gate is on the PANEL, not the link. A transparent link on a hidden
     panel is not the whole problem — the panel's own photographs still take
     the click, and later panels stack above earlier ones, so panel six's
     images sat over panel two's link. */
  main .lc-cyc-panel{pointer-events:none}
  main .lc-cyc-panel[data-current]{pointer-events:auto}
  html:not(.lcjs) main .lc-cyc-panel{pointer-events:auto}
  main .lc-cyc-link:focus-visible{outline:3px solid var(--mint);outline-offset:-8px}
  /* Across the seam, not inside one half — that straddle is the composition. */
  main .lc-cyc-label{position:absolute;left:50%;top:50%;z-index:2;margin:0;
    transform:translate(-50%,-50%);text-align:center;pointer-events:none;
    max-width:min(90%,20ch);color:#fff;font-weight:700;
    font-size:clamp(22px,3.4vw,46px);line-height:1.1;letter-spacing:.02em;
    text-shadow:0 2px 26px rgba(3,15,38,.75)}
  /* "Active pursuit" under the market name. A pill rather than more heading:
     it is a status, not part of the title, and it has to read as a caption at
     a glance over a photograph. */
  main .lc-cyc-tag{display:block;width:fit-content;margin:14px auto 0;
    padding:5px 13px;border:1px solid rgba(255,255,255,.42);border-radius:var(--r-pill);
    background:rgba(3,15,38,.42);backdrop-filter:blur(6px);
    font-size:11.5px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
    line-height:1;text-shadow:none;color:rgba(255,255,255,.94)}
  main .lc-cyc-key{display:grid;gap:clamp(14px,2vw,26px);
    grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
    margin-top:clamp(26px,4vw,48px);padding-top:clamp(18px,2.4vw,28px);
    border-top:1px solid var(--edge);transition:border-color var(--mo-ui) ease}
  main .lc-cyc-keylab{margin:0 0 6px;font-size:11.5px;font-weight:700;
    letter-spacing:.14em;text-transform:uppercase;color:var(--fg-accent);
    transition:color var(--mo-ui) ease}
  main .lc-cyc-keylist{margin:0;font-size:14.5px;line-height:1.6;color:var(--fg-dim);
    transition:color var(--mo-ui) ease}

  /* WITHOUT THE SCRUB, THE SIX STACK. This used to hide every panel but the
     first, which stopped them piling up and was the wrong trade: html.lcjs is
     never added under REDUCED MOTION, so a reader with that setting on saw
     "Utility Scale Solar" and no sign that five other markets existed — on the
     one page whose whole job is showing the breadth of the work. Losing an
     animation is acceptable; losing five sixths of the content is not.

     So they come out of the absolute stack and become a list. Each panel keeps
     its two-up composition and its label across the seam; the section simply
     grows to hold all six and is scrolled past normally. */
  html:not(.lcjs) main .lc-cyc-stage{height:auto;overflow:visible;
    display:grid;gap:2px;background:none}
  html:not(.lcjs) main .lc-cyc-panel{position:relative;inset:auto;
    height:clamp(300px,52vh,520px);background:var(--stage)}
  /* The parallax offset is a scroll product and there is no scroll driving it,
     so the images sit where they are cropped rather than at a frozen offset. */
  html:not(.lcjs) main .lc-cyc-half img{transform:none}
  html.lcjs main .lc-cyc-track{position:relative;
    height:calc(100vh + (var(--n,1) - 1) * 62vh)}
  html.lcjs main .lc-cyc-stage{position:sticky;top:0;height:100vh}

  @media (max-width:760px){
    html.lcjs main .lc-cyc-track{height:calc(100vh + (var(--n,1) - 1) * 52vh)}
    main .lc-cyc-label{font-size:clamp(18px,5.2vw,28px)}
  }

  /* ── contact ──────────────────────────────────────────────────── */
  main .lc-contact{display:grid;gap:22px;
    grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
  main .lc-contact-item{border-top:1px solid var(--edge);padding-top:16px;
    transition:border-color var(--mo-ui) ease}
  main .lc-contact-item p,main .lc-contact-item a{margin:0;font-size:16.5px;
    line-height:1.6;color:var(--fg);transition:color var(--mo-ui) ease}
  /* A LINK HAS TO LOOK LIKE ONE (audit, 13 Aug 2026). The phone number and the
     two addresses on this page rendered as plain body text in the same colour
     and weight as the postal address beside them, with no underline — so the
     one page whose entire job is to be contacted gave no sign that any of it
     was clickable. The same addresses on Careers were teal and underlined.
     This is that treatment, which is also the site's own .lc-links style. */
  main .lc-contact-item a{color:var(--fg-accent);text-decoration:none;
    border-bottom:1px solid currentColor;padding-bottom:1px;
    transition:color var(--mo-ui) ease,opacity var(--mo-ui) ease}
  main .lc-contact-item a:hover{opacity:.7}
  main .lc-contact-item .mlabel{margin:0 0 6px;font-size:11px;font-weight:700;
    letter-spacing:.14em;text-transform:uppercase;color:var(--fg-accent)}

  /* ── the market-page parallax opener ──────────────────────────────
     Full bleed, type over the frame. It cancels .lc-masthead's padding the
     same way the expanding opener does — that padding reserves room for the
     fixed cap above a copy block, and here the cap sits over the photograph. */
  main .lc-sec.lc-masthead.lc-phero{padding:0;overflow:hidden}
  /* `main section > div` caps direct div children at 1100px — the same rule
     .lc-x-track has to beat. Without this the "full bleed" opener is a 1100px
     letterbox with the ground showing either side of it. */
  main .lc-phero > .lc-phero-stage{max-width:none;margin:0}
  main .lc-phero-stage{position:relative;width:100%;
    height:clamp(430px,68vh,720px);overflow:hidden;background:var(--stage)}
  /* Oversized and offset so there is travel in both directions to give away.
     124% with -12% top means the frame is never short of the stage at either
     end of the scrub, so no band of background can appear. */
  main .lc-phero-media{position:absolute;left:0;top:-12%;width:100%;height:124%;
    object-fit:cover;object-position:center;
    transform:translate3d(0,var(--py,0%),0);will-change:transform}
  /* Two stops doing two jobs: the top band keeps the header legible, the
     bottom one carries the type. Between them the photograph is untouched. */
  /* THE FLOOR HAS TO REACH THE TOP OF THE COPY, not just the bottom of the
     stage (13 Aug 2026). The lower gradient faded out at 62%, and on the pages
     with a two-line headline the eyebrow and the first line sit ABOVE that —
     measured against white type, Transmission & Substations ran 1.93:1 on the
     eyebrow and 2.69:1 on the HEADLINE, Mission Critical 2.27 and 2.31.
     That is the same failure the client reported on the What We Do opener,
     on four of the six market pages. Extended and deepened so the whole copy
     block has ground under it whatever the photograph does. */
  main .lc-phero-scrim{position:absolute;inset:0;pointer-events:none;
    background:
      linear-gradient(to bottom,rgba(3,15,38,.88),rgba(3,15,38,.34) 26%,rgba(3,15,38,0) 46%),
      linear-gradient(to top,rgba(3,15,38,.92),rgba(3,15,38,.70) 34%,
        rgba(3,15,38,.44) 62%,rgba(3,15,38,.16) 82%,rgba(3,15,38,0) 96%)}
  main .lc-phero-copy{position:absolute;left:0;right:0;bottom:0;
    padding-bottom:clamp(34px,5vh,64px)}
  main .lc-phero-copy .eyebrow{margin:0 0 12px;color:rgba(255,255,255,.94)}
  main .lc-phero-copy h1{margin:0;max-width:16ch;color:#fff;
    font-size:var(--t-h1);font-weight:700;line-height:1.03;
    letter-spacing:-.032em;text-shadow:0 2px 30px rgba(3,15,38,.5)}
  main .lc-phero-copy .lede{margin:18px 0 0;max-width:52ch;
    color:rgba(255,255,255,.9);font-size:var(--t-lede);line-height:1.55;
    text-shadow:0 2px 18px rgba(3,15,38,.55)}
  @media (max-width:680px){
    main .lc-phero-stage{height:clamp(380px,62vh,520px)}
  }

  /* ── the inquiry form ─────────────────────────────────────────────
     Underlined fields rather than boxes: the page is type on Porcelain with
     hairline rules between the contact items directly above, and a stack of
     bordered input boxes reads as an admin screen dropped into it. The rule
     under each field is the same 1px var(--edge) those items use. */
  /* Held to a measure rather than run to the full container. A 1000px-wide
     single-line text input reads as a database screen; this is the same
     comfortable measure the prose blocks use.
     TWO COLUMNS, NOT auto-fit. auto-fit resolved to three here and left an
     orphan on every row that had four fields — phone alone, stage alone — which
     is what made the block look unfinished. Fixed pairs mean every row is
     deliberate: name/company, email/phone, and so on down. */
  /* THE FORM CARRIES ITS OWN LIGHT PLATE (David, 12 Aug 2026).
     An interior page crosses from Porcelain to Imperial between .42 and .64 of
     its scroll, and on Contact the form sits astride that crossing: you began
     filling it in on light ground and finished on navy, passing through a
     murky grey-blue in the middle. That alone was only ugly. The real failure
     was the dropdowns — a <select> hands its own `color` to the option list the
     OS draws, so the moment --fg went pale the choices were white on white and
     could not be read at all.
     Pinning the six ground tokens to their light values on the form fixes both
     at once: every rule below already reads them, so nothing else changes, and
     the form stops being a passenger on the journey and becomes a plate. */
  /* Full measure rather than 820px. At 1440 the form filled the left 57% of
     the section and the entire right half sat empty for its full height — on
     the one page whose job is to be filled in. It now spans the same measure
     as the heading above it, which puts each field at roughly 470px: still a
     comfortable input width, and the composition is no longer lopsided. */
  main .lc-form{margin-top:clamp(26px,4vw,44px);max-width:none;
    --fg:rgb(8,39,103);--fg-dim:rgb(70,70,63);--fg-accent:rgb(0,161,175);
    --edge:rgba(8,39,103,.20);--panel:#fff;
    /* --now IS PART OF THE PLATE, and leaving it out was an incomplete fix.
       Pinning a surface means pinning BOTH sides of every pair on it. .pill
       paints `background:var(--fg); color:var(--now)` — so with --fg pinned to
       navy and --now still tracking the ground, the submit button's label
       followed the page down into Imperial and finished as navy on navy.
       Measured at the foot of the contact page: 1.00:1. The button was still
       there and still worked; it just had no visible label. */
    --now:var(--porcelain);
    padding:clamp(26px,3.4vw,44px);border-radius:var(--r-lg);
    background:var(--porcelain);border:1px solid rgba(8,39,103,.10);
    box-shadow:0 24px 60px rgba(3,15,38,.10)}
  main .lc-form-row{display:grid;gap:clamp(18px,2.4vw,30px) clamp(22px,3vw,38px);
    grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}
  /* The rhythm between rows lives here now rather than on each field, so it is
     one number instead of one per field plus a grid gap fighting it. */
  main .lc-form-row + .lc-form-row,
  main .lc-form-set + .lc-form-row{margin-top:clamp(22px,2.8vw,34px)}
  @media (max-width:680px){
    main .lc-form-row{grid-template-columns:minmax(0,1fr)}
  }
  /* The row gap already separates the rows. The per-field margin was adding to
     it rather than replacing it, so consecutive rows sat 44-56px apart and the
     form read as a list of unrelated questions. */
  /* max-width:none because a field is a <p> and `main p` holds paragraphs to a
     54ch reading measure. That measure is right for prose and meaningless for
     an input: it was capping every field at 611px, so the full-width ones
     stopped short of the form's own edge and the details box looked misaligned
     with everything above it. The grid decides these widths. */
  main .lc-field{margin:0;max-width:none;display:flex;flex-direction:column;gap:8px}
  main .lc-field--wide{grid-column:1/-1}
  main .lc-field label{font-size:11.5px;font-weight:700;letter-spacing:.14em;
    text-transform:uppercase;color:var(--fg-dim);transition:color var(--mo-ui) ease}
  main .lc-req{color:var(--fg-accent)}
  main .lc-hint{font-size:13px;line-height:1.5;color:var(--fg-dim);opacity:.8}
  main .lc-field input,
  main .lc-field select,
  main .lc-field textarea{width:100%;padding:11px 2px;border:0;
    border-bottom:1px solid var(--edge);border-radius:0;background:transparent;
    color:var(--fg);font:inherit;font-size:16.5px;line-height:1.5;
    transition:border-color var(--mo-ui) ease,color var(--mo-ui) ease}
  main .lc-field textarea{resize:vertical;min-height:120px}
  /* The arrow has to be drawn: stripping the border strips the native chrome
     with it on every browser that draws one. */
  main .lc-field select{appearance:none;padding-right:26px;cursor:pointer;
    background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
      linear-gradient(135deg,currentColor 50%,transparent 50%);
    background-position:calc(100% - 13px) calc(50% + 2px),calc(100% - 8px) calc(50% + 2px);
    background-size:5px 5px,5px 5px;background-repeat:no-repeat}
  /* Stated rather than inherited. The plate above already keeps --fg dark, but
     the option list is drawn by the OS and some browsers take their colours
     from the system rather than the element — so the one control that was
     unreadable gets its colours written down. */
  main .lc-field select option{color:rgb(8,39,103);background:#fff}
  /* A full-measure select puts its arrow 400px from the word it belongs to and
     reads as a text field that happens to have a triangle. Held to the width of
     the answer instead. */
  main .lc-field--pick{max-width:340px}
  main .lc-field input:focus-visible,
  main .lc-field select:focus-visible,
  main .lc-field textarea:focus-visible{outline:none;border-bottom-color:var(--fg-accent)}
  main .lc-field input:user-invalid,
  main .lc-field textarea:user-invalid{border-bottom-color:#c2410c}
  /* Space above the legend, not below it: the group has to read as a new
     section of the form rather than as a caption on the row above it. */
  main .lc-form-set{margin:clamp(24px,3vw,36px) 0 0;padding:0;border:0}
  main .lc-form-set legend{padding:0 0 14px;font-size:11.5px;font-weight:700;
    letter-spacing:.14em;text-transform:uppercase;color:var(--fg-accent)}
  /* The send row was the one row with no rhythm above it: margin:0 put the
     button hard against the bottom of the Details hint, 0px where every other
     row in the form is separated by 34. A little MORE than the row gap here,
     because this is the end of the form rather than another question. */
  main .lc-form-send{display:flex;align-items:center;gap:20px;flex-wrap:wrap;
    margin:clamp(30px,3.8vw,46px) 0 0}
  main .lc-form-send .pill{margin-top:0;border:0;cursor:pointer;font-family:inherit}
  main .lc-form-fine{font-size:13px;color:var(--fg-dim)}
  /* Off-screen rather than display:none — a bot reading the DOM should find a
     field that looks ordinary, and a screen reader should never reach it. */
  main .lc-form-gate{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
  main .lc-form-note{margin:0 0 clamp(22px,3vw,34px);padding:clamp(18px,2.4vw,26px);
    border-radius:var(--r-md);border:1px solid var(--edge);background:var(--panel)}
  main .lc-form-note p{margin:0 0 8px;font-size:16px;line-height:1.6;color:var(--fg)}
  main .lc-form-note p:last-child{margin-bottom:0;color:var(--fg-dim)}
  main .lc-form-note--ok{border-color:rgba(127,208,215,.55)}
  main .lc-form-note--bad{border-color:rgba(194,65,12,.45)}
  /* Script hides the group you are not filling in; with no script both stay,
     which is why each carries a legend naming its audience. */
  html.lcjs main .lc-form-set[hidden]{display:none}

  /* ── the What We Do index ─────────────────────────────────────────
     THE CARDS ARE THE HOMEPAGE'S CARDS. Everything that makes them look the
     way they look — the 3:4 plate, the veil, the sheen, the glass caption, the
     hover lift and the copy that opens on hover — comes from `main .card` and
     its children further up this file, and nothing is restated here. The old
     .lc-scope rules (a 96px thumbnail beside two lines of text) are gone
     rather than left dead; they were the second, diverging definition this
     change exists to remove.

     ONLY THE LAYOUT IS LOCAL, and only because it has to be. The homepage
     presents these in a pinned horizontal rail that arrives as a fanned pile;
     an index page wants to show all six at once and be done. So the same cards
     are laid into a grid, and the two rail-specific properties — the fixed
     card width and the flex sizing that a horizontal scroller needs — are the
     only things overridden. */
  main .lc-scopes{display:grid;gap:clamp(16px,2vw,24px);
    grid-template-columns:repeat(auto-fill,minmax(258px,1fr))}
  /* Beats `main .card` (0,1,1) and the narrow `main .card{width:min(76vw,300px)}`
     on specificity (0,2,1), so it holds at every breakpoint without !important
     and without caring where it sits in the file. */
  main .lc-scopes .card{width:auto;flex:initial;display:block;
    text-decoration:none;color:inherit}

  /* ── awaiting-content note ────────────────────────────────────────
     Visually distinct from real copy on purpose. A section that is honestly
     empty should not be mistakable for one that is finished. */
  main .lc-note{border:1px dashed var(--edge);border-radius:var(--r-lg);
    padding:clamp(26px,4vw,40px);max-width:74ch;
    transition:border-color var(--mo-ui) ease}
  main .lc-note p{margin:0 0 14px;font-size:16px;line-height:1.65;color:var(--fg-dim);
    max-width:none;transition:color var(--mo-ui) ease}
  main .lc-note p:last-child{margin-bottom:0}
  main .lc-note h2{font-size:clamp(22px,2.6vw,32px)}

  /* ── closing CTA ──────────────────────────────────────────────── */
  main .lc-cta-sec{padding-bottom:var(--sp-close)}
  main .lc-cta{text-align:center;max-width:640px;margin:0 auto}
  main .lc-cta h2{margin:0 auto 12px;max-width:22ch}
  main .lc-cta p{margin:0 auto;max-width:46ch;font-size:16.5px;line-height:var(--lh-body);
    color:var(--fg-dim);transition:color var(--mo-ui) ease}
  main .lc-cta .pill{margin-top:26px}

  @media (max-width:760px){
    /* SHORTHAND, AND IT CLOBBERS. This resets padding on all four sides, and
       .lc-phero-copy is a .lc-in — so the phone rule silently zeroed the
       parallax hero's padding-bottom and the standfirst sat flush on the bottom
       edge of every service page. Same specificity, later in the file, so it
       won. Anything that gives a .lc-in vertical padding has to restate it
       below this line. */
    main .lc-in{padding:0 clamp(18px,5vw,40px)}
    /* Restated for exactly that reason. A touch more than the desktop clamp
       gives, because the copy runs to the full width of a phone and needs the
       bottom edge further away to read as seated rather than dropped. */
    main .lc-phero-copy{padding-bottom:clamp(38px,6.5vh,64px)}
    main .lc-masthead h1{max-width:none}
    /* The .lc-scope stacking rules that were here are gone with the rows they
       stacked. The grid needs no phone case of its own: auto-fill drops to one
       column on its own, and the card's own narrow rules — copy already open,
       arrow already shown — come from .card and apply here for free. */
  }

  /* ── long-form editor content ─────────────────────────────────────
     The Privacy Policy is the first page whose copy lives in post_content
     rather than the theme registry — legal text is revised by people who
     should not need a deploy to do it. So .lc-prose has to style whatever
     the editor emits, not just the handful of tags the registry produces.
     These rules are generic on purpose: any future editor-authored page
     picks them up. */
  main .lc-prose h2{margin:44px 0 14px;font-size:clamp(22px,2.6vw,32px);
    letter-spacing:-.022em;line-height:1.14;max-width:26ch}
  main .lc-prose h2:first-child{margin-top:0}
  /* A REGISTRY HEADING IS A SECTION HEADING, wherever it lands (Katie, 13 Aug
     2026: "make sure the header font is the same for 'The experience behind the
     build.' and 'Leadership.'"). It was not — 32px against 46px — because a
     prose block's heading was inheriting the long-form scale above, which
     exists for editor-authored body copy and is deliberately smaller.
     .lc-rise is the tell: louth_callan_block_head() puts it on every heading it
     emits, and the editor cannot. So registry headings take the section scale
     and the Privacy Policy keeps its own. */
  main .lc-prose h2.lc-rise{font-size:var(--t-h2);letter-spacing:-.028em;
    line-height:1.06;max-width:20ch}
  main .lc-prose h3{margin:30px 0 10px;font-size:17.5px;color:var(--fg-accent)}
  main .lc-prose ul{margin:0 0 18px;padding:0;list-style:none;max-width:66ch}
  main .lc-prose li{position:relative;margin:0 0 10px;padding-left:18px;
    font-size:16px;line-height:1.62;color:var(--fg-dim);transition:color var(--mo-ui) ease}
  main .lc-prose li::before{content:'';position:absolute;left:0;top:.62em;
    width:5px;height:5px;border-radius:50%;background:var(--fg-accent)}
  main .lc-prose a{color:var(--fg-accent);text-decoration:none;
    border-bottom:1px solid currentColor}
  main .lc-prose strong{color:var(--fg);font-weight:700}
  /* the "Last updated" stamp reads as metadata, not as the opening sentence */
  main .lc-legal-date{font-size:14px;letter-spacing:.02em;opacity:.75;
    margin-bottom:26px}

  /* the footer baseline now carries a link as well as the copyright */
  #site-foot .fbase a{color:inherit;text-decoration:none;transition:color var(--mo-ui) ease}
  #site-foot .fbase a:hover{color:var(--porcelain)}
