/* ==========================================================================
   SPT Maju, v3 "Engineered Precision"
   Swiss/technical direction. Customer's locked brand colours only:
   #4E6981  #A0C9EE  #FFFFFF  #313131
   ========================================================================== */

:root{
  /* customer locked palette, no additions */
  --navy:#4E6981;          /* headings, brand, primary */
  --ink:#3D5265;           /* body copy, 8.1:1 on white */
  --navy-deep:#33485C;     /* deepest surface, derived shade */
  --lblue:#A0C9EE;         /* accent, hover/markers/decorative */
  --lblue-tint:#EAF2FA;
  --lblue-deep:#7AB4E0;    /* deeper shade for hover states */
  --c313:#313131;          /* body text (locked spot) */

  --white:#fff;
  --surface:#EEF3F8;
  --muted:#5B6B7A;         /* 5.5:1 on white */
  --line:#DBE3EC;
  --line-strong:#C3D0DD;
  --navy-hover:#28394A;    /* derived: brand-cell hover, darker than --navy-deep */
  --on-navy-soft:#DCEBF9;  /* derived: status text on navy, 8.6:1 */
  --on-navy:#E4E9EC;       /* derived: secondary text on locked footer navy, 4.69:1 AA */
  --navy-ink:#22313F;      /* derived: deepest ground, hero scrim, white 8.5:1 over it */

  --head:"Helvetica Neue",Helvetica,Arial,"Segoe UI",sans-serif;
  --body:"Barlow","Helvetica Neue",Helvetica,Arial,sans-serif;
  --mono:"Barlow Semi Condensed","Barlow",monospace;

  --wrap:1280px;
  --gut:clamp(22px,5vw,64px);
  --nav-h:88px;

  --r-sm:2px; --r-md:4px; --r-lg:6px;
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:calc(var(--nav-h) + 12px);font-size:112.5%}
body{
  margin:0;font-family:var(--body);font-weight:400;
  color:var(--ink);background:var(--white);line-height:1.72;
  overflow-x:hidden;-webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block;height:auto}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
h1,h2,h3,h4{font-family:var(--head);font-weight:700;color:var(--navy);margin:0;line-height:1.06;letter-spacing:-.02em}

.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gut)}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ---------- the signature: index-numbered section eyebrow ---------- */
.eyebrow{
  display:flex;align-items:center;gap:14px;
  font-family:var(--body);font-weight:600;font-size:.82rem;
  letter-spacing:.22em;text-transform:uppercase;color:var(--navy);
  margin-bottom:clamp(22px,3vw,38px);
}
/* index number, navy on light / white on dark, AA compliant */
.eyebrow .idx{
  font-weight:700;color:var(--navy);font-size:.86rem;letter-spacing:.1em;
  position:relative;padding-right:2px;
}
.eyebrow .idx::after{content:" /";color:var(--line-strong);font-weight:400}
.eyebrow .rule{height:1.5px;width:clamp(28px,4vw,56px);background:var(--lblue);flex:0 0 auto}
.eyebrow.on-dark{color:#fff}
.eyebrow.on-dark .idx{color:#fff}
.eyebrow.on-dark .idx::after{color:rgba(255,255,255,.45)}

/* hairline section divider */
.section{padding:clamp(70px,9vw,132px) 0;position:relative}
.section + .section{border-top:1px solid var(--line)}
.h-title{font-size:clamp(2.1rem,4.6vw,3.6rem);letter-spacing:-.03em}
.h-sub{font-family:var(--body);font-weight:400;font-size:clamp(1.02rem,1.4vw,1.16rem);color:var(--muted);line-height:1.7;max-width:60ch}

/* corner tick marks, blueprint feel */
.ticked{position:relative}
.ticked::before,.ticked::after{
  content:"";position:absolute;width:11px;height:11px;pointer-events:none;
  border-color:var(--lblue);border-style:solid;border-width:0;
}
.ticked::before{top:0;left:0;border-top-width:1.5px;border-left-width:1.5px}
.ticked::after{bottom:0;right:0;border-bottom-width:1.5px;border-right-width:1.5px}

/* ==========================================================================
   HEADER
   ========================================================================== */
.site-header{
  position:fixed;inset:0 0 auto 0;z-index:120;background:rgba(255,255,255,.94);
  backdrop-filter:blur(10px);border-bottom:1px solid var(--line);
  transition:transform .38s var(--ease),box-shadow .3s;will-change:transform;
}
.site-header.is-hidden{transform:translateY(calc(-100% - 2px))}
.site-header.is-stuck{box-shadow:0 1px 0 var(--line),0 12px 30px rgba(51,72,92,.06)}
/* header content shares the same rail as .wrap so the logo aligns with section content */
.header-inner{
  max-width:var(--wrap);margin-inline:auto;padding:0 var(--gut);height:var(--nav-h);
  display:flex;align-items:center;gap:clamp(16px,3vw,40px);
}
.brand{flex:0 0 auto;display:flex;align-items:center}
/* size the stacked logo by HEIGHT so it fits inside the nav bar instead of
   overflowing (width-based sizing made it ~96px tall in an 88px header) */
.brand img{height:clamp(50px,5vw,62px);width:auto}
.menu{display:flex;align-items:center;gap:clamp(14px,2vw,32px);margin:0 auto 0 clamp(10px,3vw,48px);padding:0;list-style:none}
.menu a{
  font-family:var(--body);font-weight:500;font-size:.96rem;letter-spacing:.02em;
  color:var(--navy);padding:8px 0;position:relative;white-space:nowrap;
}
.menu a::after{content:"";position:absolute;left:0;right:100%;bottom:2px;height:2px;background:var(--lblue);transition:right .32s var(--ease)}
.menu a:hover::after,.menu a[aria-current="page"]::after{right:0}
.menu a[aria-current="page"]{color:var(--navy)}

.header-links{display:flex;align-items:center;gap:clamp(12px,1.5vw,20px);flex:0 0 auto}
.header-links a{display:flex;align-items:center;opacity:.9;transition:opacity .2s,transform .2s}
.header-links a:hover{opacity:1;transform:translateY(-1px)}
.header-links img{height:22px;width:auto}
.header-links .li img{height:19px}
.header-links .li svg{width:19px;height:19px;fill:var(--navy)}

.burger{display:none;width:42px;height:42px;flex:0 0 auto;align-items:center;justify-content:center}
.burger span{display:block;width:22px;height:2px;background:var(--navy);position:relative;transition:background .2s}
.burger span::before,.burger span::after{content:"";position:absolute;left:0;width:22px;height:2px;background:var(--navy);transition:transform .28s var(--ease)}
.burger span::before{top:-7px}.burger span::after{top:7px}
.burger.is-open span{background:transparent}
.burger.is-open span::before{transform:translateY(7px) rotate(45deg)}
.burger.is-open span::after{transform:translateY(-7px) rotate(-45deg)}

/* ==========================================================================
   1 · HERO, asymmetric 5/7, offset photo, big headline
   ========================================================================== */
.hero{margin-top:var(--nav-h);position:relative;overflow:hidden}
/* ---------- cinematic hero: full-bleed photograph, copy over a navy scrim ----------
   The aerial is a wide sweep, so it runs edge to edge instead of being cropped into
   a column. Contrast is guaranteed by the scrim rather than by luck: it holds .86
   alpha across the copy zone, so even over the brightest water white reads 8.5:1
   and the light blue 4.9:1. */
/* SITE RULE: every hero banner is the same height as the homepage's.
   This is `height`, not `min-height`, with a minimum, a page whose headline
   wraps to more lines silently grows its own banner (services was running 785
   against the homepage's 720). One value here governs every .hero-cine, and
   .hero-body's padding below is sized so the LONGEST headline on the site still
   fits inside it. If a future page overflows, shorten the copy or reduce that
   padding, do not reintroduce min-height. */
.hero-cine{
  position:relative;isolation:isolate;
  height:clamp(460px,72vh,720px);display:flex;align-items:center;
  background:var(--navy-ink);
}
.hero-media{position:absolute;inset:0;overflow:hidden;background:var(--navy-ink);z-index:0}
.hero-media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* md-pcb - generated circuit-board hero (yewho-web-effects P16). Sits in the
   same slot as .hero-media so the
   scrim, seam and copy layers are untouched. */
.md-pcb{position:absolute;inset:0;z-index:0;overflow:hidden;background:var(--navy-ink)}
.md-pcb svg{width:100%;height:100%;display:block}
/* Sources squarer than 2:1 are widened in the FILE, not here: their canvas is
   extended to 2:1 with a blurred stretch of their own left edge, so `cover`
   fits exactly and the subject sits at the smaller scale without cropping.
   CSS contain+mask was tried first and rejected, the picture only emerges
   where the scrim thins, so a flat wall edge reads as a seam no fade removes. */
/* Scrim strength is set by the brightest hero photograph, which is the homepage's
   daylight aerial: open sea and sky sit directly under the copy. At the previous
   .94/.86/.34/.08 that image left the lead line at 3.46:1, the sea is far
   brighter there than the shot it replaced. This ramp brings it to 4.70:1 with
   the heading at 5.00:1. */
.hero-scrim{
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(100deg,
    rgba(34,49,63,.97) 0%, rgba(34,49,63,.95) 44%,
    rgba(34,49,63,.46) 68%, rgba(34,49,63,.12) 88%, transparent 100%);
}
/* The services hero used a lighter scrim because its photograph was already
   dark. That photograph is gone - a generated board sits there now, which is
   brighter - so it takes the standard ramp like every other hero. */
/* measurement ticks, now down the right edge of the band */
.hero-seam{position:absolute;top:0;bottom:0;right:0;width:1px;background:var(--lblue);z-index:2;opacity:.6}
.hero-seam span{position:absolute;right:0;width:9px;height:1px;background:var(--lblue)}
.hero-inner{position:relative;z-index:2;width:100%}
/* Padding is capped at 60, not 96: the services headline needs 593px of content
   height, so 2 x 60 is the most that still fits the 720 banner. The homepage
   copy is centred and unchanged by this, only the space around it shrinks. */
.hero-body{
  display:flex;flex-direction:column;align-items:flex-start;
  padding:clamp(40px,4.4vw,60px) 0;max-width:60%;
}
.hero-body h1{
  font-size:clamp(2.4rem,5.4vw,4.5rem);letter-spacing:-.035em;line-height:.98;
  color:#fff;margin:0;
}
/* Sub-line, a sibling of the h1, not a child, so the typewriter types the
   headline only. On the scrim it needs near-white, not the light-grey muted. */
.hero-body .g{
  display:block;font-size:clamp(1rem,1.5vw,1.24rem);font-weight:500;letter-spacing:.01em;
  color:rgba(255,255,255,.9);margin:22px 0 clamp(26px,3.2vw,38px);
  font-family:var(--body);line-height:1.6;max-width:46ch;
}
@media (max-width:900px){
  /* height:auto, not min-height:0, the desktop rule now sets a fixed height,
     which on a narrow screen would clip a headline that wraps to four lines. */
  .hero-cine{height:auto}
  .hero-body{max-width:100%;padding:clamp(56px,12vw,92px) 0}
  /* on a narrow screen the gradient runs out of room, so darken the whole frame */
  .hero-scrim{background:linear-gradient(180deg,rgba(34,49,63,.82),rgba(34,49,63,.92))}
}
.hero-rule{width:72px;height:2px;background:var(--lblue);margin-bottom:28px}
.hero-meta{display:flex;flex-wrap:wrap;gap:10px 30px;margin-bottom:clamp(32px,4vw,44px);font-family:var(--body);font-weight:600;font-size:.82rem;letter-spacing:.14em;text-transform:uppercase;color:var(--navy)}
.hero-meta span{display:flex;align-items:center;gap:9px}
.hero-meta span::before{content:"";width:6px;height:6px;background:var(--lblue);flex:0 0 auto}
.hero-cta{display:inline-flex;align-items:center;gap:14px;align-self:flex-start;
  font-family:var(--body);font-weight:600;font-size:1rem;letter-spacing:.02em;color:var(--navy-deep);
  background:var(--lblue);padding:15px 30px;border-radius:var(--r-sm);
  transition:background .26s,transform .26s var(--ease),gap .26s var(--ease),box-shadow .26s}
.hero-cta:hover{background:var(--lblue-deep);transform:translateY(-2px);gap:20px;box-shadow:0 12px 28px rgba(160,201,238,.35)}
.hero-cta .arw{width:20px;height:12px;position:relative}
.hero-cta .arw::before{content:"";position:absolute;left:0;top:5px;width:100%;height:2px;background:var(--navy-deep)}
.hero-cta .arw::after{content:"";position:absolute;right:0;top:1px;width:9px;height:9px;border-top:2px solid var(--navy-deep);border-right:2px solid var(--navy-deep);transform:rotate(45deg)}

/* stat strip contained to the same rail as sections so "09" aligns with the eyebrows below */
.hero-stats{max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gut);display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--line)}
/* The strip spans the same 1280 measure as the menu, and each cell centres its
   own content within its quarter of it, so the four blocks read as an even rank
   rather than a left-aligned list. Padding is symmetric for the same reason; the
   earlier right-only version put the content on an even pitch but hard against
   the left of each column. The light-blue tick centres with it. */
.hero-stats .cell{
  padding:clamp(22px,3vw,34px) clamp(14px,1.8vw,26px);
  border-right:1px solid var(--line);position:relative;text-align:center;
}
.hero-stats .cell::after{
  content:"";position:absolute;top:0;left:50%;transform:translateX(-50%);
  width:32px;height:2px;background:var(--lblue);
}
.hero-stats .cell:last-child{border-right:0}
.hero-stats .num{font-family:var(--head);font-weight:700;font-size:clamp(1.8rem,3vw,2.6rem);color:var(--navy);letter-spacing:-.02em;line-height:1}
.hero-stats .num .u{color:var(--lblue);font-size:.6em;vertical-align:top;margin-left:2px}
.hero-stats .lbl{font-family:var(--body);font-weight:500;font-size:.8rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin-top:10px}

/* ==========================================================================
   2 · CLIENTS, precise horizontal rail with index ticks
   Customer brief: "gradient from light blue to darker blue, left to right"
   ========================================================================== */
/* Backdrop: a Sarawak offshore gas platform, the infrastructure these clients
   actually operate, sits low and right, under the customer's locked light-blue
   gradient. The gradient stays a separate layer rather than folding into the
   shorthand so its strength is tunable in one place.
   .68 is measured, not chosen by eye: the eyebrow is 14.8px normal-weight navy
   (--navy #4E6981), so it needs 4.5:1, and the darkest pixel anywhere under the
   copy reaches that only from ~.65 up. Worth knowing the ceiling is ~4.8:1,
   #4E6981 on the plain #EAF2FA gradient scored that before any photo existed,
   so the backdrop costs the section almost nothing in contrast. */
.clients-band{
  position:relative;isolation:isolate;
  background:var(--surface) url(../img/clients-bg.webp) center 72%/cover no-repeat;
}
.clients-band::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(90deg,var(--lblue-tint) 0%,var(--surface) 100%);
  opacity:.68;
}
.clients-band > .wrap{position:relative;z-index:1}
/* Portrait: the landscape frame loses ~55% of its width to a cover crop, so bias
   the focal point right or the platform is cropped clean out of the picture. */
@media (max-width:820px){.clients-band{background-position:78% 70%}}
/* Stepped auto-slider: exactly --rail-n cards fill the track, and the JS
   advances one whole card at a time to a snapped position, so a partial card
   is never left at the edge (which a continuous marquee cannot avoid). No edge
   mask is needed for the same reason. */
.rail{position:relative}
.rail-track{
  /* Three client logos. At 4 the track was one slot short of a full set, so
     the seamless-loop clone in v3.js sat inside the viewport and the first
     logo appeared twice at rest. Raise this again if a fourth client is
     added. */
  --rail-n:3;                                   /* whole cards visible */
  --rail-gap:clamp(16px,2.4vw,30px);
  display:flex;gap:var(--rail-gap);overflow-x:auto;scroll-behavior:smooth;
  scroll-snap-type:x mandatory;padding:4px 0 16px;
  scrollbar-width:none;-ms-overflow-style:none;
}
@media (max-width:1080px){.rail-track{--rail-n:3}}
@media (max-width:820px){.rail-track{--rail-n:2}}
@media (max-width:520px){.rail-track{--rail-n:1}}
.rail-track::-webkit-scrollbar{display:none}
.client-card{
  /* exactly --rail-n cards span the track, gaps included */
  flex:0 0 auto;scroll-snap-align:start;position:relative;
  width:calc((100% - (var(--rail-n) - 1) * var(--rail-gap)) / var(--rail-n));
  aspect-ratio:1.42;height:auto;
  background:#fff;border:1px solid var(--line);display:flex;align-items:center;justify-content:center;padding:30px 26px;
  transition:border-color .3s,box-shadow .3s,transform .3s var(--ease);
}
.client-card:hover{border-color:var(--lblue);box-shadow:0 14px 34px rgba(51,72,92,.1);transform:translateY(-4px)}
.client-card img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain}
/* All three marks are height-constrained, so they align on a common height,
   the standard logo-wall treatment. They only fill 55-73% of the card's inner
   box though, so the padding is trimmed to give each mark more presence.
   Per-logo optical scaling is deliberately NOT applied here: the tall Petronas
   mark already occupies the least area, so shrinking it would make the row
   less balanced, not more. Worth a visual pass once the real logos are final. */
.client-card{padding:22px 20px}
/* If the rail has too few cards to overflow (the reduced-motion path skips the
   marquee clones), centre them rather than stranding them at the left edge.
   `safe` keeps the start reachable when it does overflow. */
.rail-track{justify-content:safe center}

.rail-nav{display:flex;gap:8px}
.rail-btn{width:44px;height:44px;display:grid;place-items:center;color:var(--navy);background:#fff;border:1px solid var(--line);border-radius:var(--r-sm);transition:border-color .22s,color .22s,background .22s}
.rail-btn:hover{border-color:var(--navy);background:var(--navy);color:#fff}
.rail-btn svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:2}
.rail-head{margin-bottom:clamp(30px,4vw,48px)}
.rail-foot{display:flex;justify-content:flex-end;margin-top:clamp(22px,3vw,34px)}

/* ==========================================================================
   3 · BRANDS, distinct from clients: navy band, circular, spec captions
   ========================================================================== */
/* brands background uses the deeper navy (not a locked colour, the brief only
   specifies white circle frames) so muted role captions clear AA */
/* Backdrop: a control-panel backplate carrying the very hardware these agencies
   cover, DIN-rail terminal blocks, PLC modules, contactors. The four brand cells
   are opaque navy, so the photograph is only ever seen in the band behind the
   heading, the gutters either side of the strip, and below it; the composition is
   built for that, with the equipment kept right and low.
   The scrim is a left-heavy horizontal ramp, not a flat wash, for a measured
   reason: the longer heading line reaches x~1008 while the PLC block starts at
   x~680, so a flat tint either crushes the equipment or leaves white type on a
   near-white highlight (#EBF5FD raw). This ramp holds the heading at 4.98:1 and
   the eyebrow at 14:1, both full AA, not just AA-large, and still lets the
   right side read at .42. Keep it at 90deg: the ratios above were measured on a
   purely horizontal ramp, and tilting it changes the alpha under the type. */
.brands{
  background:var(--navy-deep) url(../img/brands-bg.webp) center 42%/cover no-repeat;
  color:#fff;
}
.brands::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(90deg,
    rgba(34,49,63,.92) 0%,
    rgba(34,49,63,.86) 48%,
    rgba(34,49,63,.62) 72%,
    rgba(34,49,63,.42) 100%);
}
/* Portrait keeps only ~17% of the frame's width, so aim the crop at the terminal
   -block cluster rather than the empty dark left edge. Holds 9.1:1. */
@media (max-width:820px){.brands{background-position:45% 42%}}
.brands .h-title{color:#fff}
.brand-strip{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.14)}
.brand-cell{background:var(--navy-deep);padding:clamp(24px,2.8vw,38px) clamp(16px,1.8vw,26px);display:flex;flex-direction:column;align-items:center;text-align:center;gap:18px;transition:background .3s}
.brand-cell:hover{background:var(--navy-hover);transform:translateY(-2px)}
.brand-cell{transition:background .3s,transform .3s var(--ease)}
/* rectangular white frame, fits the wide brand marks larger than a circle did */
.brand-chip{width:100%;height:clamp(96px,10vw,128px);border-radius:var(--r-sm);background:#fff;display:flex;align-items:center;justify-content:center;padding:clamp(16px,1.8vw,26px)}
.brand-chip img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain}
.brand-cell .nm{font-family:var(--body);font-weight:600;font-size:.92rem;letter-spacing:.06em;text-transform:uppercase;color:#fff}
.brand-cell .rl{font-family:var(--body);font-weight:400;font-size:.84rem;color:rgba(255,255,255,.8);letter-spacing:.02em}

/* ==========================================================================
   4 · SERVICES, indexed 3-col, hairline separators
   ========================================================================== */
/* Backdrop scoped to the heading band ONLY, never the grid. The nine .svc cards
   have no background of their own, so their 15.4px --ink copy sits straight on
   the section; holding it at 4.5:1 across the whole grid would need a wash so
   pale the photograph disappears anyway. The band above the grid carries only
   the eyebrow, heading and lead line, so that is where the image lives.
   The wash is a horizontal ramp because a vertical one cannot work here: the
   glass wall's dark mullions run the FULL height of the frame, so no crop
   offset clears them out from under the navy heading, measured, every offset
   failed identically. Holding white at .96 out to 68% puts all three text
   colours on effectively solid white (eyebrow 5.36:1, heading 5.21:1, lead
   line 5.02:1) and lets the console read from x~980 rightward. */
/* Section-wide backdrop, sitting under everything including the nine cards. The
   .78 wash is not a taste call: the card TITLES are --navy #4E6981, LIGHTER than
   the --ink body copy, so they bind, they need the background above 0.774
   relative luminance where the body copy only needs 0.533. At .72 the titles
   fall to 4.36:1. So this photograph can only ever be a faint tint; no wash
   leaves it visible AND keeps the titles legible. The wash rides as a gradient
   layer in the same shorthand rather than a pseudo-element, because ::before and
   ::after are already spoken for by the heading band above. */
#services{
  position:relative;isolation:isolate;
  background:
    linear-gradient(rgba(255,255,255,.78),rgba(255,255,255,.78)),
    url(../img/services-grid-bg.webp) center center/cover no-repeat,
    #fff;
}
@media (max-width:820px){#services{background:#fff}}
#services::before,#services::after{
  content:"";position:absolute;top:0;left:0;right:0;
  height:clamp(360px,36vw,520px);z-index:0;pointer-events:none;
}
#services::before{background:url(../img/services-bg.webp) center 40%/cover no-repeat}
#services::after{
  background:
    linear-gradient(180deg,rgba(255,255,255,0) 0%,rgba(255,255,255,0) 72%,#fff 90%,#fff 100%),
    linear-gradient(90deg,rgba(255,255,255,.96) 0%,rgba(255,255,255,.96) 68%,rgba(255,255,255,.12) 100%);
}
#services > .wrap{position:relative;z-index:1}
/* Portrait: the copy spans the full width, so there is no right-hand margin to
   put a picture in without running the 5.5:1 lead line over it. Clean white. */
@media (max-width:820px){#services::before,#services::after{display:none}}
/* Nine equal capability cards, in the supplied design sample's card treatment:
   raised dark card on a darker ground, code badge on the image, category label
   leading the body, and a ruled foot closing every card.
   The section ground stays WHITE while the cards are dark. The sample ran both
   dark; a white ground keeps the page's light/dark alternation intact (dark
   hero, light stats, white here, dark footer band) and gives the cards far more
   lift than the sample's 1.41:1 card-to-ground separation ever did.
   Card is --navy-deep. Everything on it clears AA, title 9.45:1, body 7.72:1,
   and the light blue on the number, category and link 5.44:1. */
.svc-sec{background:#fff}
.svc-sec .h-title{font-size:clamp(1.7rem,3.2vw,2.5rem);margin-bottom:14px}
.svc-sec .h-sub{margin-bottom:clamp(30px,3.8vw,48px)}

.svc-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(14px,1.6vw,20px)}
/* On white the sample's translucent light-blue border is invisible, so the lift
   comes from a restrained shadow instead, the card's own dark fill now does the
   edge definition that border was for. */
.svc{
  display:flex;flex-direction:column;position:relative;overflow:hidden;
  background:var(--navy-deep);
  box-shadow:0 2px 10px rgba(51,72,92,.10);
  transition:transform .3s var(--ease),box-shadow .3s;
}
.svc:hover{transform:translateY(-4px);box-shadow:0 16px 38px rgba(51,72,92,.20)}

/* One consistent 16:9 image across all nine, darkened at the foot so the badge
   and the card body meet without a hard seam. */
.svc-media{position:relative;aspect-ratio:16/9;overflow:hidden;background:var(--navy-ink)}
.svc-media img{
  width:100%;height:100%;object-fit:cover;display:block;
  filter:saturate(.78) contrast(1.04);transition:transform .5s var(--ease);
}
.svc:hover .svc-media img{transform:scale(1.04)}
.svc-media::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(34,49,63,.06),rgba(34,49,63,.58));
}
.svc-code{
  position:absolute;top:14px;left:14px;z-index:2;
  font-family:var(--body);font-weight:600;font-size:.64rem;letter-spacing:.16em;
  color:var(--lblue);background:rgba(34,49,63,.82);
  border:1px solid rgba(160,201,238,.22);padding:6px 9px;
  font-variant-numeric:tabular-nums;
}

.svc-body{
  display:flex;flex-direction:column;flex:1;
  padding:clamp(22px,2.4vw,28px) clamp(20px,2.2vw,26px) clamp(20px,2.2vw,24px);
}
.svc-cat{
  font-family:var(--body);font-weight:600;font-size:.62rem;letter-spacing:.18em;
  text-transform:uppercase;color:var(--lblue);margin-bottom:11px;
}
/* Titles run 15 to 41 characters, so short ones set on one line and long ones
   on two, which pushed the description, and everything under it, to a different
   height in every card. Reserving two lines lines the whole row up. */
.svc h3{
  font-family:var(--body);font-weight:600;font-size:clamp(1.06rem,1.3vw,1.2rem);
  color:#fff;line-height:1.28;margin:0 0 12px;letter-spacing:-.005em;
  min-height:calc(2 * 1.28em);
}
/* the category label is a single word or two, but reserve its line too so the
   title starts level even where it wraps */
.svc-cat{min-height:1.2em}
.svc p{font-weight:400;font-size:.87rem;line-height:1.72;color:var(--on-navy);margin:0}

/* Ruled foot on every card, carrying the related project where one exists.
   The four services with no published project keep the rule and leave it empty
   rather than announcing the absence, the sample's "no forced project link" is
   internal language, not something a visitor should read. */
.svc-foot{
  margin-top:auto;padding-top:18px;min-height:19px;
  border-top:1px solid rgba(160,201,238,.14);
}
.svc-foot .svc-rel{
  margin:0;padding:0;border-bottom:0;color:var(--lblue);
  display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;
  font-size:.7rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
}
.svc-foot .svc-rel:hover{color:#fff}
.svc-foot .svc-rel .ar{background:currentColor}
.svc-foot .svc-rel .ar::after{border-color:currentColor}

@media (max-width:980px){
  /* Two up. Nine cards leave one on the last row at its normal width, it is
     never stretched across the pair. */
  .svc-grid{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:620px){
  .svc-grid{grid-template-columns:1fr}
  /* Shorter image on one column so nine stacked cards do not run away. */
  .svc-media{aspect-ratio:2/1}
}

/* ==========================================================================
   5 · ABOUT, asymmetric 7/5, offset logo panel
   ========================================================================== */
/* .cta-band and .pd-cta removed with their markup: both were pre-footer "Contact
   Us" panels that asked the same question the footer's conversion band asks a
   few pixels below them, on all nine pages that carried one. */

/* Backdrop: an onshore gas plant across Sarawak scrub, the midstream half of
   what the copy beside it claims. This section can carry a genuinely visible
   photograph where the services grid could not, for one structural reason: the
   copy column stops at x=766 and the logo panel is opaque, so the entire right
   side has no live text on it. The ramp therefore only has to protect the left.
   .82 is the floor, measured: the body copy is --ink at 19px needing 4.5:1, and
   it lands at 4.98:1 here, at .72 it drops to 4.16:1 and fails. The .06 tail
   leaves the plant itself effectively unwashed. */
.about-band{
  position:relative;isolation:isolate;
  background:
    linear-gradient(90deg,
      rgba(255,255,255,.82) 0%,
      rgba(255,255,255,.82) 52%,
      rgba(255,255,255,.06) 100%),
    url(../img/about-bg.webp) center center/cover no-repeat,
    #fff;
}
/* Portrait: the copy spans the full width and the panel drops beneath it, so
   there is no text-free side left to reveal the picture in. */
@media (max-width:820px){.about-band{background:#fff}}
.about-grid{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);gap:clamp(36px,6vw,90px);align-items:center}
.about-copy p{font-size:1.06rem;line-height:1.85;margin:0 0 30px;max-width:52ch}
.about-panel{position:relative;background:var(--surface);border:1px solid var(--line);padding:clamp(36px,5vw,70px);display:grid;place-items:center}
.about-panel img{width:min(80%,360px)}
.arrow-cta{display:inline-flex;align-items:center;gap:0;font-family:var(--body)}
.arrow-cta .lbl{background:var(--navy);color:#fff;font-weight:600;letter-spacing:.02em;padding:14px 32px;border-radius:var(--r-sm) 0 0 var(--r-sm);transition:background .26s}
.arrow-cta .tip{width:0;height:0;border-top:25px solid transparent;border-bottom:25px solid transparent;border-left:16px solid var(--navy);transition:border-left-color .26s}
.arrow-cta:hover .lbl{background:var(--navy-deep)}
.arrow-cta:hover .tip{border-left-color:var(--navy-deep)}

/* ==========================================================================
   6 · GUARANTEE, 3 indexed panels with icon + line
   ========================================================================== */
/* Backdrop: a plant cable-ladder run, every tray aligned and every tie at the
   same pitch, the visible evidence of the three commitments claimed beside it.
   All three .guar cards are opaque (#fff / --lblue / navy), so the only text on
   the section background is the eyebrow and the heading, both top-left.
   The wash is tinted with --surface rather than white so the held side stays
   exactly the colour this section already was. .94 is set by the HEADING, not
   the eyebrow: the heading runs out to x~740 and catches the flare tower, which
   at .84 leaves it at 3.99:1. It clears WCAG for large text there, but 4.69:1
   at .94 is what a headline over a photograph actually needs to look solid.
   Costs nothing, the held side is sky. The .05 tail leaves the cable run
   fully readable. */
.guar-band{
  position:relative;isolation:isolate;
  background:
    linear-gradient(90deg,
      rgba(238,243,248,.94) 0%,
      rgba(238,243,248,.94) 54%,
      rgba(238,243,248,.05) 100%),
    url(../img/guarantee-bg.webp) center center/cover no-repeat,
    var(--surface);
}
/* Portrait: heading spans the full width and the cards stack, so there is no
   text-free side to reveal the picture in. */
@media (max-width:820px){.guar-band{background:var(--surface)}}
.guar-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(20px,2.6vw,34px)}
.guar{border:1px solid var(--line);padding:clamp(30px,3.4vw,48px);position:relative;transition:border-color .3s,transform .3s var(--ease),box-shadow .3s;background:#fff}
.guar:hover{border-color:var(--lblue);transform:translateY(-4px);box-shadow:0 16px 40px rgba(51,72,92,.09)}
.guar .gnum{font-family:var(--head);font-weight:700;font-size:clamp(2.4rem,4vw,3.4rem);color:var(--lblue);line-height:1;letter-spacing:-.03em}
.guar .gico{width:44px;height:44px;margin:24px 0 20px;color:var(--navy);transition:transform .35s var(--ease)}
.guar:hover .gico{transform:scale(1.12)}
.guar .gico svg{width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:1.7}
.guar h3{font-family:var(--body);font-weight:600;font-size:1.24rem;color:var(--navy);margin-bottom:12px}
.guar p{font-weight:400;font-size:.95rem;color:var(--muted);line-height:1.7;margin:0}
.guar--lblue{background:var(--lblue);border-color:var(--lblue)}
.guar--lblue .gnum{color:var(--navy-deep)}
.guar--lblue .gico{color:var(--navy-deep)}
.guar--lblue h3{color:var(--navy-deep)}
.guar--lblue p{color:var(--ink)}
.guar--lblue:hover{border-color:var(--navy);background:var(--lblue-deep)}
.guar--navy{background:var(--navy);border-color:var(--navy)}
.guar--navy .gnum{color:var(--lblue)}
.guar--navy .gico{color:var(--lblue)}
.guar--navy h3{color:#fff}
.guar--navy p{color:rgba(255,255,255,.85)}
.guar--navy:hover{border-color:var(--lblue);background:var(--navy-deep)}

/* ==========================================================================
   FOOTER
   ========================================================================== */
/* Footer ground: a general-arrangement elevation of SPT Maju's own eight
   delivered projects, the marine switchboard lineup, the fire water pump panel,
   the fuel gas super heater, the genset controller, the outdoor HVAC board, the
   instrument interface panel, Curtin's monitoring cabinet and the sub-system
   control box, drawn as blueprint linework.
   This DOES take the footer off the locked flat #4E6981. Unavoidable: the
   artwork's ground is #09111B and no veil keeps a mid navy while leaving the
   drawing visible.
   Sized `100% auto` and anchored to the bottom rather than `cover`, because the
   footer is 1.44:1 while the artwork is 2.33:1, cover would scale to height and
   throw away 38% of the width, cutting units off the end of the run. Anchoring
   it bottom keeps the whole lineup. The base colour matches the artwork's own
   ground so the band above it is seamless.
   Veil .44, set by the LINEWORK rather than the ground. The ground is already
   near-black and needs nothing; the drawing's brightest lines peak at L=0.563,
   and the credential chips, the last of the service links and the whole bottom
   bar sit over the enclosure run. At .18 a line crossing a glyph gives 2.71:1.
   .40 is the floor (4.61:1); .44 lands the links at 5.12:1 and the white labels
   at 6.26:1. Linework on a black ground dims gracefully, compared against .18
   side by side, the drawing still reads. */
.site-footer{
  color:#fff;padding:clamp(64px,8vw,104px) 0 0;
  background:
    linear-gradient(rgba(0,0,0,.44),rgba(0,0,0,.44)),
    url(../img/footer-art.webp) bottom center/100% auto no-repeat,
    #09111B;
}
.foot-grid{display:grid;grid-template-columns:1.1fr 1fr;gap:clamp(40px,6vw,100px)}
.site-footer h2{color:#fff;font-size:clamp(1.8rem,3vw,2.6rem);letter-spacing:-.02em;margin-bottom:clamp(26px,3.4vw,40px)}
.site-footer .co{font-family:var(--body);font-weight:500;font-size:clamp(1.1rem,1.6vw,1.3rem);margin:0 0 clamp(24px,3vw,38px);color:#fff}
.field{margin-bottom:22px}
.field label{display:block;font-family:var(--body);font-weight:500;font-size:.86rem;letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;color:rgba(255,255,255,.92)}
.field input,.field textarea{width:100%;font-family:var(--body);font-size:1rem;color:#fff;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.22);border-radius:var(--r-sm);padding:11px 13px;outline:0;transition:border-color .22s,background .22s}
.field textarea{resize:vertical;min-height:88px}
.field input:focus,.field textarea:focus{border-color:var(--lblue);background:rgba(255,255,255,.1)}
.field input::placeholder,.field textarea::placeholder{color:rgba(255,255,255,.4)}
.submit{display:inline-flex;align-items:center;gap:12px;margin-top:6px;font-family:var(--body);font-weight:600;font-size:1rem;letter-spacing:.02em;color:var(--navy-deep);background:var(--lblue);padding:13px 32px;border-radius:var(--r-sm);transition:transform .26s var(--ease),box-shadow .26s,background .22s}
.submit:hover{transform:translateY(-2px);background:var(--lblue-deep);box-shadow:0 12px 28px rgba(160,201,238,.32)}
.form-msg{margin-top:14px;font-size:.95rem;color:var(--on-navy-soft);min-height:1.4em}
.contact-list{font-family:var(--body);font-weight:400;font-size:1.02rem;line-height:2;margin:0 0 24px;font-style:normal;color:rgba(255,255,255,.95)}
.contact-list a:hover{color:var(--lblue)}
/* labels near-white (locked #4E6981 bg needs ~90%+ for AA); the label look comes
   from the caps + letter-spacing + smaller size, not from dimming */
.contact-list .k{display:inline-block;min-width:0;color:rgba(255,255,255,.9);font-weight:500;font-size:.74rem;letter-spacing:.14em;text-transform:uppercase;margin-right:10px}
.foot-social{display:flex;align-items:center;gap:22px;margin-top:6px}
.foot-social a{display:flex;align-items:center;opacity:.92;transition:transform .24s var(--ease),opacity .24s}
.foot-social a:hover{transform:translateY(-3px);opacity:1}
.foot-social img{height:24px;width:auto}
.foot-social .li img{height:22px}
.foot-social .li svg{width:24px;height:24px;fill:#fff}
.foot-bar{margin-top:clamp(46px,6vw,80px);border-top:1px solid rgba(255,255,255,.2);padding:22px 0 28px;display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;font-family:var(--body);font-size:.88rem;color:rgba(255,255,255,.9)}
.foot-bar .tag{letter-spacing:.14em;text-transform:uppercase;font-weight:600;font-size:.78rem;color:rgba(255,255,255,.88)}
/* Agency credit. Sits between the copyright and the tagline; on narrow screens
   the bar wraps to a column and this centres with the rest. */
.foot-by{font-size:.82rem;color:rgba(255,255,255,.82)}
.foot-by a{
  color:var(--lblue);font-weight:600;letter-spacing:.06em;
  border-bottom:1px solid transparent;transition:color .24s,border-color .24s;
}
.foot-by a:hover{color:#fff;border-bottom-color:var(--lblue)}
@media (max-width:900px){
  .foot-bar{flex-direction:column;align-items:flex-start;gap:8px}
}

/* ==========================================================================
   EXPANDED FOOTER
   Background stays the customer-locked #4E6981. On that navy the locked light
   blue only reaches 3.30:1, so it is used for rules/icons/hover only, never
   as body text. Text is white (5.74:1) or --on-navy (4.69:1). The bottom bar
   drops to --navy-deep, where light blue clears AA at 5.44:1.
   ========================================================================== */
/* Conversion band. Replaced the inline enquiry form, which was a second copy of
   the contact page's form sitting on every page, and which came bundled with a
   contact block that repeated the email, phone, address and all three social
   logos already in the directory below it.
   It sits on --navy-ink and bleeds full width with a 100vw pseudo-element, the
   same technique the bottom bar already uses, html/body carry overflow-x, so
   100vw is safe here. */
/* Negative top margin cancels .site-footer's own top padding so the band starts
   flush at the footer's edge, otherwise a strip of the lighter navy shows above
   it and the band reads as floating rather than as the footer's opening. */
.foot-cta{
  position:relative;margin-top:calc(-1 * clamp(64px,8vw,104px));
  margin-bottom:clamp(40px,5vw,66px);
  padding:clamp(46px,5.4vw,68px) 0 clamp(46px,5.4vw,68px);
  display:grid;grid-template-columns:minmax(0,1.15fr) auto;
  gap:clamp(24px,3.4vw,48px);align-items:center;
}
/* Backdrop: the plant at blue hour, pipe rack in silhouette with its work lights
   still burning, the band asks people to get in touch, and a plant that is lit
   and running is what "we are here" looks like.
   The veil is BLACK, not --navy-ink, and that distinction is the whole trick.
   Compositing toward #22313F lightens the silhouette while darkening the sky, so
   raising it flattens the picture to grey, tested at .55/.70/.80 and every one
   read worse than plain navy would have. Black darkens both together and keeps
   the separation, so the band can go properly dark without going muddy.
   Darkening only ever helps the contrast here, since every text colour on this
   band is white or near-white: at .40 the numbers are eyebrow 16.6:1, heading
   13.6:1, lede 7.3:1, phone button 13.6:1.
   background-position y is 35%, which puts the rack along the lower third and
   the tower light near the buttons. */
.foot-cta::before{
  content:"";position:absolute;top:0;bottom:0;left:50%;transform:translateX(-50%);
  width:100vw;z-index:0;
  background:
    linear-gradient(rgba(0,0,0,.40),rgba(0,0,0,.40)),
    url(../img/footer-cta-bg.webp) center 35%/cover no-repeat,
    var(--navy-ink);
}
/* Portrait: the band grows to 473px tall against a 390 viewport, so a cover crop
   keeps a sliver of the frame. Flat navy-ink, which the band was designed on. */
@media (max-width:820px){.foot-cta::before{background:var(--navy-ink)}}
.foot-cta > *{position:relative;z-index:1}
.foot-cta h2{
  font-size:clamp(1.7rem,3.2vw,2.5rem);line-height:1.1;letter-spacing:-.025em;
  color:#fff;margin:0 0 14px;
}
.foot-cta p{
  font-family:var(--body);font-weight:400;font-size:1rem;line-height:1.75;
  color:var(--on-navy);margin:0;max-width:44ch;
}
.fcta-act{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
/* light blue on navy-ink is 8.9:1 for the ink label, the strongest button on
   the site, which is the point of the band */
.fcta-btn{
  background:var(--lblue);color:var(--navy-ink);
  font-family:var(--body);font-weight:700;font-size:.94rem;letter-spacing:.02em;
  padding:15px 30px;border-radius:var(--r-sm);white-space:nowrap;
  transition:background .26s,transform .26s var(--ease);
}
.fcta-btn:hover{background:#fff;transform:translateY(-2px)}
.fcta-tel{
  border:1px solid rgba(255,255,255,.34);color:#fff;
  font-family:var(--body);font-weight:600;font-size:.94rem;
  padding:14px 26px;border-radius:var(--r-sm);white-space:nowrap;
  transition:border-color .26s,background .26s;
}
.fcta-tel:hover{border-color:var(--lblue);background:rgba(160,201,238,.12)}
@media (max-width:820px){
  .foot-cta{grid-template-columns:1fr;gap:26px}
  .fcta-act{width:100%}
  .fcta-btn,.fcta-tel{flex:1 1 auto;text-align:center}
}

.foot-main{
  display:grid;grid-template-columns:1.6fr 1fr 1.2fr 1.4fr;
  gap:clamp(30px,4vw,64px);
  padding-bottom:clamp(36px,4.6vw,60px);
}
.foot-brand .fb-logo{width:clamp(150px,15vw,196px);height:auto;margin-bottom:22px}
.foot-brand .fb-line{
  font-family:var(--body);font-weight:400;font-size:.98rem;line-height:1.75;
  color:var(--on-navy);margin:0 0 24px;max-width:34ch;
}
/* credential chips, icon in light blue (decorative), label in white */
.fb-creds{display:flex;flex-direction:column;gap:10px}
.fb-cred{display:flex;align-items:center;gap:10px;font-family:var(--body);font-weight:500;font-size:.78rem;letter-spacing:.12em;text-transform:uppercase;color:#fff}
.fb-cred svg{width:16px;height:16px;flex:0 0 auto;fill:none;stroke:var(--lblue);stroke-width:2}

.foot-col h3{
  font-family:var(--body);font-weight:600;font-size:.8rem;letter-spacing:.18em;
  text-transform:uppercase;color:#fff;margin:0 0 18px;padding-bottom:12px;
  border-bottom:1px solid rgba(255,255,255,.18);position:relative;
}
.foot-col h3::after{content:"";position:absolute;left:0;bottom:-1px;width:34px;height:1px;background:var(--lblue)}
.foot-col ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:11px}
.foot-col a{
  font-family:var(--body);font-weight:400;font-size:.95rem;color:var(--on-navy);
  position:relative;display:inline-flex;align-items:center;gap:9px;
  transition:color .22s,gap .26s var(--ease);
}
.foot-col a::before{content:"";width:0;height:1px;background:var(--lblue);transition:width .28s var(--ease)}
.foot-col a:hover{color:#fff}
.foot-col a:hover::before{width:12px}

.foot-contact .fc-list{font-family:var(--body);font-weight:400;font-size:.95rem;line-height:1.9;font-style:normal;color:var(--on-navy);margin:0 0 20px}
.foot-contact .fc-list .k{display:block;font-weight:600;font-size:.7rem;letter-spacing:.16em;text-transform:uppercase;color:#fff;margin-top:12px}
.foot-contact .fc-list a:hover{color:#fff}
.foot-contact .fc-cta{
  display:inline-flex;align-items:center;gap:12px;margin-top:6px;
  font-family:var(--body);font-weight:600;font-size:.9rem;letter-spacing:.02em;
  color:var(--navy-deep);background:var(--lblue);padding:11px 22px;border-radius:var(--r-sm);
  transition:background .24s,transform .24s var(--ease),gap .24s var(--ease);
}
.foot-contact .fc-cta:hover{background:#fff;transform:translateY(-2px);gap:16px}
.foot-contact .fc-cta .ar{width:16px;height:1.5px;background:currentColor;flex:0 0 auto}

/* footer hosts the blueprint-beam backdrop */
.site-footer{position:relative;overflow:hidden}
.site-footer .foot-beams{position:absolute;inset:0;z-index:0}
.site-footer .wrap{position:relative;z-index:1}

/* separator between the enquiry band and the columns (homepage only) */
.foot-sep{height:1px;background:rgba(255,255,255,.16);margin:clamp(38px,5vw,62px) 0}

/* Bottom bar sits on the deeper navy. It lives inside the constrained .wrap,
   so the colour is bled to the viewport with a 100vw pseudo-element rather
   than by restructuring the markup. html{overflow-x:clip} keeps 100vw safe. */
.site-footer .foot-bar{margin-top:0;position:relative;border-top:0}
/* The bar used to be --navy-deep, which was darker than the flat navy above it.
   Now that the body carries the dark texture, --navy-deep would read LIGHTER
   than the body and the footer's tonal order would invert. A black wash keeps
   the bar reading as the deepest strip while letting the texture run through it,
   so the whole footer stays one material. */
.site-footer .foot-bar::before{
  content:"";position:absolute;top:0;bottom:0;left:50%;transform:translateX(-50%);
  width:100vw;background:rgba(0,0,0,.28);border-top:1px solid rgba(255,255,255,.14);
}
.site-footer .foot-bar > *{position:relative}
.site-footer .foot-bar .tag{color:var(--lblue)}

@media (max-width:1080px){
  .foot-main{grid-template-columns:1fr 1fr;gap:36px}
  .foot-brand{grid-column:1 / -1}
}
@media (max-width:560px){
  .foot-main{grid-template-columns:1fr}
}

/* ---------- credential strip (Trust & Authority) ---------- */
.cred-strip{max-width:var(--wrap);margin-inline:auto;padding:0 var(--gut);display:flex;flex-wrap:wrap;gap:0}
.cred-strip .cred{display:flex;align-items:center;gap:10px;padding:clamp(14px,2vw,20px) clamp(16px,2.4vw,32px);font-family:var(--body);font-weight:500;font-size:.82rem;letter-spacing:.1em;text-transform:uppercase;color:var(--navy);border-bottom:1px solid var(--line);flex:1 1 auto;min-width:180px}
.cred-strip .cred-ico{width:20px;height:20px;flex:0 0 auto;color:var(--lblue)}
.cred-strip .cred-ico svg{width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:2}

/* ==========================================================================
   CONTACT PAGE  (standalone, light background)
   ========================================================================== */
.contact-body{display:grid;grid-template-columns:1.1fr 1fr;gap:clamp(40px,6vw,100px);align-items:start}
.contact-body h2{font-size:clamp(1.8rem,3vw,2.6rem);letter-spacing:-.02em;margin-bottom:clamp(26px,3.4vw,40px);color:var(--navy)}

/* form fields, light background variant */
.contact-body .cfield{margin-bottom:22px}
.contact-body .cfield label{display:block;font-family:var(--body);font-weight:500;font-size:.86rem;letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;color:var(--navy)}
.contact-body .cfield input,.contact-body .cfield textarea{width:100%;font-family:var(--body);font-size:1rem;color:var(--ink);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);padding:12px 14px;outline:0;transition:border-color .22s,background .22s,box-shadow .22s}
.contact-body .cfield textarea{resize:vertical;min-height:110px}
.contact-body .cfield input:focus,.contact-body .cfield textarea:focus{border-color:var(--navy);background:#fff;box-shadow:0 0 0 3px rgba(78,105,129,.1)}
.contact-body .cfield input::placeholder,.contact-body .cfield textarea::placeholder{color:var(--muted)}

/* submit button, navy */
.contact-body .cbtn{display:inline-flex;align-items:center;gap:12px;margin-top:6px;font-family:var(--body);font-weight:600;font-size:1rem;letter-spacing:.02em;color:#fff;background:var(--navy);padding:14px 34px;border-radius:var(--r-sm);border:0;cursor:pointer;transition:transform .26s var(--ease),box-shadow .26s,background .22s}
.contact-body .cbtn:hover{transform:translateY(-2px);background:var(--navy-deep);box-shadow:0 12px 28px rgba(51,72,92,.22)}
.contact-body .cmsg{margin-top:14px;font-size:.95rem;color:var(--muted);min-height:1.4em}

/* company details, right column */
.contact-body .co-name{font-family:var(--body);font-weight:600;font-size:clamp(1.1rem,1.6vw,1.3rem);color:var(--navy);margin:0 0 clamp(20px,2.6vw,32px)}
.contact-info{font-family:var(--body);font-weight:400;font-size:1.02rem;line-height:2.2;margin:0 0 24px;font-style:normal;color:var(--ink)}
.contact-info a{transition:color .22s}
.contact-info a:hover{color:var(--lblue-deep)}
.contact-info .k{display:inline-block;min-width:0;color:var(--navy);font-weight:600;font-size:.74rem;letter-spacing:.14em;text-transform:uppercase;margin-right:10px}
.contact-social{display:flex;align-items:center;gap:22px;margin-top:clamp(20px,2.4vw,32px)}
.contact-social a{display:flex;align-items:center;opacity:.85;transition:transform .24s var(--ease),opacity .24s}
.contact-social a:hover{transform:translateY(-3px);opacity:1}
.contact-social img{height:24px;width:auto}
.contact-social .li svg{width:24px;height:24px;fill:var(--navy)}

@media (max-width:1080px){
  .contact-body{grid-template-columns:1fr;gap:48px}
}


/* ---------- focus-visible (a11y) ---------- */
a:focus-visible,.submit:focus-visible,.rail-btn:focus-visible,.burger:focus-visible,.totop:focus-visible,.hero-cta:focus-visible{outline:2px solid var(--navy);outline-offset:3px}
.field input:focus-visible,.field textarea:focus-visible{outline:2px solid var(--lblue);outline-offset:1px}

/* ---------- reveal ---------- */
.reveal{opacity:0;transform:translateY(22px);transition:opacity .7s var(--ease-out),transform .7s var(--ease-out)}
.reveal.in{opacity:1;transform:none}
.reveal[data-d="1"]{transition-delay:.08s}
.reveal[data-d="2"]{transition-delay:.16s}
.reveal[data-d="3"]{transition-delay:.24s}
/* the new sections stagger five and six items, so the ladder runs further */
.reveal[data-d="4"]{transition-delay:.32s}
.reveal[data-d="5"]{transition-delay:.40s}
.reveal[data-d="6"]{transition-delay:.48s}
.no-js .reveal{opacity:1;transform:none}

/* ---------- back to top ---------- */
.totop{position:fixed;right:22px;bottom:22px;z-index:110;width:46px;height:46px;display:grid;place-items:center;background:var(--navy);color:#fff;border:1px solid var(--navy);border-radius:var(--r-sm);box-shadow:0 10px 26px rgba(51,72,92,.28);opacity:0;visibility:hidden;transform:translateY(10px);transition:opacity .3s,visibility .3s,transform .3s var(--ease),background .22s}
.totop.show{opacity:1;visibility:visible;transform:none}
.totop:hover{background:var(--navy-deep)}
.totop svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2.2}

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */
@media (max-width:1080px){
  :root{--nav-h:74px}
  .burger{display:flex}
  .menu{position:absolute;left:0;right:0;top:100%;flex-direction:column;align-items:stretch;gap:0;background:#fff;border-bottom:1px solid var(--line);margin:0;max-height:0;overflow:hidden;transition:max-height .4s var(--ease)}
  .menu.is-open{max-height:75vh;overflow:auto;box-shadow:0 20px 40px rgba(51,72,92,.12)}
  .menu li{border-top:1px solid var(--line)}
  .menu a{padding:15px 24px;font-size:1rem}
  .menu a::after{display:none}
  .header-links{display:none}
  .hero-seam{display:none}
  .brand-strip{grid-template-columns:repeat(2,1fr)}
  .svc-grid{grid-template-columns:repeat(2,1fr)}
  .svc:nth-child(3n){border-right:1px solid var(--line)}
  .svc:nth-child(2n){border-right:0}
  .about-grid{grid-template-columns:1fr;gap:36px}
  .about-panel{order:-1}
  .about-panel img{width:min(56%,280px)}
  .guar-grid{grid-template-columns:1fr}
  .foot-grid{grid-template-columns:1fr}
  .hero-stats{grid-template-columns:repeat(2,1fr)}
  .hero-stats .cell:nth-child(2n){border-right:0}
  .hero-stats .cell:nth-child(-n+2){border-bottom:1px solid var(--line)}
  .cred-strip{flex-direction:column}
  .cred-strip .cred{min-width:0}
}
@media (max-width:560px){
  .svc-grid{grid-template-columns:1fr}
  .svc{border-right:0!important}
  .brand-strip{grid-template-columns:1fr}
  .hero-stats{grid-template-columns:1fr}
  .hero-stats .cell{border-right:0;border-bottom:1px solid var(--line)}
  .foot-bar{flex-direction:column}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
  html{scroll-behavior:auto}.reveal{opacity:1;transform:none}
}

/* ==========================================================================
   INNER PAGES  (About · Projects · News)
   ========================================================================== */

/* page intro band, asymmetric, aligned to the rail */
.page-intro{margin-top:var(--nav-h);padding:clamp(56px,7vw,104px) 0 clamp(40px,5vw,64px)}
.page-intro .h-title{font-size:clamp(2.4rem,5.4vw,4.2rem)}
.intro-grid{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);gap:clamp(34px,5vw,80px);align-items:end}
.intro-lead{font-family:var(--body);font-weight:400;font-size:clamp(1.04rem,1.5vw,1.22rem);line-height:1.8;color:var(--ink);max-width:56ch}
.intro-media{position:relative;overflow:hidden;background:var(--navy-deep);min-height:clamp(240px,26vw,340px)}
.intro-media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}

/* About intro. Own classes, not .intro-grid, that one is shared with the
   Industry Coverage row further down the page.
   Both columns stay inside the content column, so the photograph's right edge
   lines up with the last menu item above it, .header-inner and .wrap share the
   same max-width and gutter. Held at the file's native 4:3 so nothing is
   cropped: the keyboard and the ladder-logic tablet both survive, and those
   details are the point of the picture. */
/* About lede: the body copy that no longer fits in the hero banner. Two columns
   so the two paragraphs sit side by side rather than running one long measure. */
.about-lede{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(26px,3.4vw,52px);align-items:start;
}
.about-lede .intro-lead{max-width:none;margin:0}
@media (max-width:820px){.about-lede{grid-template-columns:1fr;gap:20px}}
/* Fixed 2x2, not flex-wrap: the four together are wider than the copy column,
   so wrapping strands "Automation" alone on a second row. A grid reads as a
   deliberate block of four and mirrors the sentence above it. */
.intro-disc{
  list-style:none;margin:clamp(20px,2.4vw,28px) 0 0;padding:0;
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px;
  max-width:440px;
}
.intro-disc li{
  font-family:var(--body);font-weight:500;font-size:.8rem;letter-spacing:.03em;
  color:var(--navy);border:1px solid var(--line-strong);padding:8px 14px;
}
@media (max-width:420px){.intro-disc{grid-template-columns:1fr}}

/* ---------- SPLIT rows (Vision / Mission) ---------- */
.split{display:grid;grid-template-columns:1fr 1fr;align-items:stretch;min-height:clamp(340px,42vw,500px);border:1px solid var(--line);border-bottom:0}
.split:last-of-type{border-bottom:1px solid var(--line)}
.split.flip .split-media{order:2}
.split-media{position:relative;background:var(--navy-deep) center/cover no-repeat;min-height:280px;display:flex;align-items:flex-end;padding:clamp(26px,3vw,42px)}
.split-media::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(20,32,44,.15),rgba(20,32,44,.5))}
/* Vision only. Measured under the white stamp on the vessel photograph: the
   sunlit deck reaches L 0.32 at the shared .5 scrim, giving 2.84:1, below even
   the 3:1 large-text floor. At .70 it measures 0.181, so 4.55:1. Mission keeps
   the shared scrim; its photograph already clears 9.16:1. */
#vision .split-media::after{background:linear-gradient(180deg,rgba(20,32,44,.24),rgba(20,32,44,.7))}
/* Mission's vessel sits in the top-right of a square photograph. The panel is
   near-square on desktop so centre is fine, but the 344x220 mobile panel crops
   36% of the height, centred, that leaves only a strip of hull. Anchoring to
   the top keeps the whole ship at every width. */
#mission .split-media{background-position:center top}
.split-stamp{position:relative;z-index:2;color:#fff;font-family:var(--head);font-weight:700;font-size:clamp(2rem,4.4vw,3.4rem);letter-spacing:-.02em;line-height:1}
.split-stamp .ul{border-bottom:2px solid var(--lblue);padding-bottom:8px}
.split-copy{display:flex;flex-direction:column;justify-content:center;padding:clamp(34px,4.6vw,72px);background:#fff}
.split.flip .split-copy{background:var(--surface)}
.split-copy .k{font-family:var(--body);font-weight:600;font-size:.8rem;letter-spacing:.2em;text-transform:uppercase;color:var(--navy);margin-bottom:18px;display:flex;align-items:center;gap:12px}
.split-copy .k::before{content:"";width:22px;height:1.5px;background:var(--lblue)}
.split-copy p,.split-copy li{font-family:var(--body);font-weight:400;font-size:1.04rem;line-height:1.8;color:var(--ink)}
.split-copy ul{margin:0;padding-left:1.15em}
.split-copy li{margin-bottom:14px}
.split-copy li::marker{color:var(--navy)}

/* ---------- What We Do + PDF ---------- */
.wwd-grid{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);gap:clamp(34px,5vw,80px);align-items:center}
.wwd-grid p{font-family:var(--body);font-weight:400;font-size:1.05rem;line-height:1.85;color:var(--ink);margin:0 0 22px;max-width:52ch}
.pdf-link{display:inline-flex;align-items:center;gap:14px;margin-top:8px;font-family:var(--body);font-weight:500;color:var(--navy)}
.pdf-link .ico{width:42px;height:54px;flex:0 0 auto;color:var(--navy)}
.pdf-link .nm{border-bottom:1px solid transparent;transition:border-color .25s}
.pdf-link:hover .nm{border-color:var(--navy)}
.pdf-link.disabled{opacity:.55;cursor:default}
/* Industry Coverage band. Deliberately NOT a background behind the copy: the
   heading needs bg luminance 0.499 and the lead 0.533, but the tower steel and
   forest measure 0.075 and 0.002, so a true background would need an 80%
   --surface wash, enough to erase the photograph. Below the text it carries no
   contrast constraint and shows at full strength.
   object-position 30% keeps the main tower whole and the river in frame; the
   2.6:1 desktop band would be a 133px sliver on a phone, so mobile takes 4:3. */
.cov-band{position:relative;overflow:hidden;margin:clamp(30px,4vw,48px) 0 0;background:var(--navy-deep);aspect-ratio:2.6/1}
.cov-band img{display:block;width:100%;height:100%;object-fit:cover;object-position:center 30%}
@media (max-width:820px){.cov-band{aspect-ratio:4/3}}
.wwd-media{position:relative;border:1px solid var(--line);overflow:hidden}
.wwd-media img{width:100%;display:block}

/* ---------- Capability matrix ---------- */
.cap-chain{width:100%;border:1px solid var(--line);margin-bottom:clamp(36px,4.6vw,60px)}
/* Real gaps, not a hairline grid: six dark tiles butted edge to edge read as one
   collage because each card's photograph runs into its neighbour's. White gutters
   give every capability its own frame, the same separation the source sheet had. */
.cap-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(10px,1.3vw,18px)}
/* Dark cells, to sit with the asset-chain banner directly above them. Each one
   carries its own blue-hour render (background-image set inline per card,
   inline, not via a custom property: a relative url() stored in a var and
   consumed here would resolve against THIS stylesheet's path and 404). Every
   pixel of those files is rolled off to luminance 0.076 in linear space, half
   the 0.1407 ceiling --on-navy needs at 4.5:1, so the photograph stays behind
   the words instead of competing with them, and no scrim is required.
   Hover must set background-COLOR only, the shorthand would discard the image. */
.cap{padding:clamp(26px,3vw,40px) clamp(20px,2.4vw,32px);border:1px solid rgba(160,201,238,.16);display:flex;flex-direction:column;transition:background-color .3s,border-color .3s;background-color:var(--navy-ink);background-size:cover;background-position:center}
.cap:hover{background-color:var(--navy-deep);border-color:rgba(160,201,238,.4)}
.cap .cnum{font-family:var(--body);font-weight:700;font-size:.8rem;letter-spacing:.14em;color:var(--lblue);margin-bottom:12px}
.cap h3{font-family:var(--head);font-weight:700;font-size:1.02rem;letter-spacing:.01em;text-transform:uppercase;color:#fff;margin-bottom:16px;line-height:1.25}
.cap ul{margin:0 0 auto;padding-left:1.05em}
.cap li{font-family:var(--body);font-weight:400;color:var(--on-navy);font-size:.95rem;line-height:1.7;margin-bottom:6px}
.cap li::marker{color:var(--lblue)}
.cap-icons{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;margin-top:clamp(20px,2.4vw,28px);padding-top:clamp(18px,2.2vw,24px);border-top:1px dashed rgba(160,201,238,.28)}
.cap-ico{display:flex;flex-direction:column;align-items:center;text-align:center;gap:8px}
.cap-ico svg{width:clamp(34px,3.6vw,46px);height:clamp(34px,3.6vw,46px);display:block}
.cap-ico em{font-style:normal;font-weight:500;font-size:.78rem;line-height:1.3;color:var(--on-navy-soft)}
.sprite{position:absolute;width:0;height:0;overflow:hidden}

/* ---------- Principal Brands (About) ---------- */
.principal{display:grid;grid-template-columns:repeat(2,1fr);gap:0;border-top:1px solid var(--line);border-left:1px solid var(--line)}
.principal .item{border-bottom:1px solid var(--line);border-right:1px solid var(--line);padding:clamp(30px,3.6vw,50px);display:flex;align-items:center;gap:clamp(22px,3vw,40px);transition:background .3s}
.principal .item:hover{background:var(--surface)}
.principal .item img{height:52px;width:auto;max-width:170px;object-fit:contain;flex:0 0 auto}
.principal .item p{font-family:var(--body);font-weight:400;font-size:.98rem;line-height:1.6;color:var(--ink);margin:0}

/* ---------- Projects ---------- */
/* ---------- Project Evidence Gallery ----------
   Eight alternating case-study rows, not cards. The image is the evidence, so it
   takes the larger share of the row; the copy column is capped near 52ch so the
   summaries stay readable rather than stretching with the viewport. */
.pj-index{margin:clamp(26px,3.4vw,40px) 0 clamp(34px,4.4vw,56px)}
.pj-index ul{
  list-style:none;margin:0;padding:0 0 14px;display:flex;flex-wrap:wrap;gap:8px 10px;
  border-bottom:1px solid var(--line);
}
.pj-index li{
  font-family:var(--body);font-weight:600;font-size:.68rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted);
  border:1px solid var(--line);padding:6px 12px;
}
.pj-index li:first-child{color:var(--navy);border-color:var(--line-strong)}

/* Rows sit in full-bleed bands, not inside .wrap. Banding inside the wrap reads
   as stripes drawn on a page; banding the full width reads as sections of one.
   Each gallery restarts the count, so 01 and 05 both open on white. */
.pj-gallery{display:block}
.pj-band{padding:clamp(48px,6.2vw,88px) 0}
.pj-band:nth-child(even){background:var(--surface)}
.pj-sec{padding-bottom:0}
.pj-sec--b{padding-top:0}
.pj-band .pj-row{margin:0}
/* Equal tracks, deliberately. An asymmetric split (42/58) put the divide at 457px
   on odd rows and 631px on even ones, 174px apart, so the alternation read as a
   ragged central axis rather than a rhythm. Equal columns place the seam at the
   same x on every row; the image and the copy simply swap sides across it.
   The 544px track also sets the copy measure, so no separate cap is needed. */
.pj-row{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(28px,4.4vw,72px);align-items:center;
}
.pj-row--flip .pj-copy{order:2}
.pj-num{display:flex;align-items:baseline;gap:8px;margin-bottom:16px}
.pj-num .n{
  font-family:var(--head);font-weight:700;font-size:clamp(2.1rem,3.4vw,3rem);
  line-height:1;color:var(--navy);letter-spacing:-.03em;font-variant-numeric:tabular-nums;
}
.pj-num .of{font-family:var(--body);font-weight:600;font-size:.8rem;letter-spacing:.1em;color:var(--line-strong)}
.pj-cat{
  display:block;font-family:var(--body);font-weight:600;font-size:.7rem;letter-spacing:.16em;
  text-transform:uppercase;color:var(--navy);margin-bottom:12px;
}
.pj-row h3{
  font-family:var(--head);font-weight:700;letter-spacing:-.02em;line-height:1.16;
  font-size:clamp(1.4rem,2.3vw,1.95rem);color:var(--navy);margin:0 0 14px;text-wrap:balance;
}
.pj-row p{
  font-family:var(--body);font-weight:400;font-size:clamp(.97rem,1.2vw,1.05rem);
  line-height:1.78;color:var(--ink);margin:0 0 clamp(18px,2.2vw,24px);
}
.pj-scope{
  list-style:none;margin:0 0 clamp(20px,2.4vw,28px);padding:0;
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;
  background:var(--line);border:1px solid var(--line);
}
.pj-scope li{
  background:#fff;padding:9px 12px;
  font-family:var(--body);font-weight:600;font-size:.68rem;letter-spacing:.09em;
  text-transform:uppercase;color:var(--muted);
}
.pj-link{
  display:inline-flex;align-items:center;gap:12px;
  font-family:var(--body);font-weight:600;font-size:.78rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--navy);
}
.pj-link .ar{width:26px;height:1.5px;background:var(--lblue);transition:width .3s var(--ease)}
.pj-link:hover .ar{width:44px}
.pj-shot{margin:0;overflow:hidden;background:var(--navy-ink);aspect-ratio:3/2}
.pj-shot img{
  width:100%;height:100%;object-fit:cover;display:block;
  filter:saturate(.86);transition:transform .6s var(--ease);
}
.pj-row:hover .pj-shot img{transform:scale(1.03)}

/* ---------- Project delivery sequence ----------
   One rule with six nodes on it. The connector is a pseudo-element on the list,
   so the markup stays an ordered list and the sequence reads in order to a
   screen reader without any arrow glyphs to announce. */
/* The sequence is drawn as a flow scene on desktop and a CSS spine on mobile.
   Three-class selectors on trace/eflow are deliberate: fx.css declares
   `.scene .eflow` at the same specificity but loads later, so a two-class
   override loses and the current renders in SyncTech red at flow.css's dash. */
/* ---------- dark interlude ----------
   Project Delivery moves between projects 04 and 05 and flips dark, splitting
   the gallery run in half and giving the page a third dark moment between the
   hero and the footer. Every ink colour inverts with it. */
.pd-dark{background:var(--navy-ink);position:relative;overflow:hidden}
.pd-art{
  position:absolute;inset:0;z-index:0;opacity:.32;pointer-events:none;
  /* masked off the copy: the light-blue eyebrow needs the ground at 0.084 and
     the board measures 0.115, so it may not sit behind any text */
  -webkit-mask-image:linear-gradient(90deg,transparent 34%,#000 62%);
          mask-image:linear-gradient(90deg,transparent 34%,#000 62%);
}
.pd-art svg{width:100%;height:100%;display:block}
.pd-in{position:relative;z-index:1}
.pd-dark .eyebrow span{color:var(--lblue)}
.pd-dark .eyebrow .rule{background:rgba(160,201,238,.4)}
.pd-dark .h-title{color:#fff}
.pd-dark .h-sub{color:var(--on-navy)}
.pd-dark .pd-flow::before{background:rgba(198,210,222,.45)}
.pd-dark .pd-label{color:var(--on-navy-soft)}
.pd-dark .pd-dot{background:var(--navy-ink);border-color:var(--lblue)}
.pd-dark .pd-scene .trace{stroke:rgba(198,210,222,.5)}
.pd-dark .pd-scene .nd-core{fill:var(--lblue)}
.pd-dark .pd-scene .nd-halo{fill:var(--lblue);opacity:.22}
.pd-scene{margin-top:clamp(30px,3.8vw,46px)}
.pd-scene svg{overflow:visible;width:100%;height:auto;display:block}
.pd-sec .pd-scene .trace{fill:none;stroke:var(--line-strong);stroke-width:1.5;stroke-linecap:round}
.pd-sec .pd-scene .eflow{
  stroke:var(--lblue);stroke-width:2.5;
  stroke-dasharray:80 1080;stroke-dashoffset:80;
  animation:md-flow 6.5s linear infinite;
}
.pd-scene .nd{--pop:rgba(160,201,238,.75)}
.pd-scene .nd-halo{fill:var(--lblue);opacity:.18}
.pd-scene .nd-core{fill:var(--navy)}
.pd-scene .nd-pulse{fill:var(--lblue)}

.pd-flow{
  list-style:none;margin:14px 0 0;padding:0;position:relative;
  display:grid;grid-template-columns:repeat(6,1fr);gap:0;
}
.pd-step{position:relative;padding:0 18px 0 0}
.pd-dot{display:none}                     /* the SVG carries the nodes on desktop */
.pd-label{
  display:block;font-family:var(--body);font-weight:600;font-size:.74rem;
  letter-spacing:.08em;text-transform:uppercase;color:var(--navy);line-height:1.45;
}

@media (max-width:1080px){
  /* Tablet keeps the alternating read but widens the copy so neither column
     collapses into a cramped half. */
  .pj-row{gap:clamp(24px,3.4vw,44px)}
  .pd-flow{grid-template-columns:repeat(3,1fr);row-gap:26px}
  .pd-scene{display:none}
  .pd-dot{display:block;position:absolute;top:2px;left:0;width:9px;height:9px;border-radius:50%;background:var(--surface);border:2px solid var(--navy)}
  .pd-step{padding:0 18px 0 22px}
}
@media (max-width:760px){
  /* Mobile order is fixed by the brief: image first, then number, category,
     title, summary, scope, CTA. order:0 on the figure overrides the flip. */
  .pj-row{grid-template-columns:1fr;gap:20px}
  .pj-row .pj-shot{order:0}
  .pj-row .pj-copy,.pj-row--flip .pj-copy{order:1}
  .pj-gallery{gap:clamp(46px,9vw,64px)}
  .pd-flow{grid-template-columns:1fr;row-gap:0}
  /* vertical spine replaces the scene once the row cannot hold six nodes */
  .pd-flow::before{content:"";position:absolute;left:4px;top:4px;bottom:14px;width:1px;background:var(--line-strong)}
  .pd-step{padding:0 0 22px 26px}
}
@media (max-width:620px){
  /* Both category labels resolve under the 12px readability floor on a phone. */
  .pj-cat,.svc-cat{font-size:.78rem;letter-spacing:.13em}
}
@media (max-width:460px){
  .pj-scope{grid-template-columns:1fr}
}
/* The View Project link is the only per-project control on a phone, so give it a
   44px touch height rather than leaving it at the 24px the text alone occupies. */
@media (hover:none){
  .pj-link{min-height:44px;align-items:center}
}

/* triangle accent (kept from brief, but a real visible element) */
.tri-accent{position:absolute;right:0;top:0;width:clamp(140px,16vw,240px);aspect-ratio:1;background:linear-gradient(150deg,var(--lblue),transparent 60%);clip-path:polygon(100% 0,100% 100%,0 0);opacity:.6;pointer-events:none}
.tri-accent::after{content:"";position:absolute;top:22px;right:22px;width:38px;height:38px;border-top:1.5px solid rgba(255,255,255,.6);border-right:1.5px solid rgba(255,255,255,.6)}

/* ==========================================================================
   PROJECT DETAIL PAGE
   Brief: title centred, name + description left, image zoom on hover,
   prev/next nav, and an index to jump straight to any project.
   ========================================================================== */
.pd-head{margin-top:var(--nav-h);padding:clamp(40px,5.5vw,80px) 0 clamp(22px,3vw,34px);text-align:center}
.pd-crumb{display:flex;align-items:center;justify-content:center;gap:10px;font-family:var(--body);font-weight:500;font-size:.8rem;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);margin-bottom:clamp(18px,2.4vw,28px)}
.pd-crumb a{color:var(--navy);border-bottom:1px solid transparent;transition:border-color .22s}
.pd-crumb a:hover{border-color:var(--navy)}
.pd-crumb .sep{color:var(--line-strong)}
.pd-title{font-family:var(--head);font-weight:700;font-size:clamp(1.9rem,4.2vw,3.3rem);letter-spacing:-.03em;line-height:1.06;color:var(--navy);margin:0 auto;max-width:20ch}
.pd-tags{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin-top:clamp(18px,2.4vw,26px)}

/* ==========================================================================
   PROJECT DETAIL, product-detail (PDP) layout
   Main viewer + thumbnail strip on the left, copy and scope on the right.
   ========================================================================== */
.pd-shop-wrap{margin-top:var(--nav-h);padding:clamp(28px,4vw,56px) 0 clamp(40px,5vw,72px)}

/* --- layout B: gallery hero across the top --- */
.pd-hero{margin-top:clamp(18px,2.4vw,30px)}
.pd-hero .pd-viewer{aspect-ratio:21/8}
.pd-below{
  display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);
  gap:clamp(28px,4vw,58px);align-items:start;
  margin-top:clamp(26px,3.4vw,44px);
}
/* the title now has a ~640px column, so it can carry real size again */
.pd-below .pd-title{
  text-align:left;margin:0 0 clamp(14px,1.8vw,20px);max-width:22ch;
  font-size:clamp(1.6rem,2.8vw,2.5rem);line-height:1.08;letter-spacing:-.025em;
}
.pd-below .pd-tags{justify-content:flex-start;margin:0 0 clamp(20px,2.6vw,28px)}
@media (max-width:900px){
  .pd-below{grid-template-columns:1fr;gap:32px}
  .pd-hero .pd-viewer{aspect-ratio:16/9}
}

.pd-viewer{
  margin:0;position:relative;overflow:hidden;background:var(--surface);
  border:1px solid var(--line);aspect-ratio:2/1;display:grid;place-items:center;
  cursor:zoom-in;
}
/* contain, not cover, these photographs run from 1.9:1 to 3.1:1, and a fixed
   box means switching thumbnails never shifts the page */
.pd-viewer img{width:100%;height:100%;object-fit:contain;display:block;transition:opacity .25s var(--ease)}
.pd-viewer.is-swapping img{opacity:0}

.pd-thumbs{display:flex;flex-wrap:wrap;gap:10px;margin-top:12px}
.pd-thumb{
  flex:0 0 auto;width:clamp(74px,8vw,96px);aspect-ratio:3/2;padding:0;overflow:hidden;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);
  cursor:pointer;transition:border-color .25s,box-shadow .25s,transform .25s var(--ease);
}
.pd-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.pd-thumb:hover{border-color:var(--lblue);transform:translateY(-2px)}
.pd-thumb.is-active{border-color:var(--navy);box-shadow:0 0 0 2px var(--navy) inset}

.pd-desc{margin-bottom:clamp(22px,3vw,32px)}
.pd-desc p{font-family:var(--body);font-weight:400;font-size:1.02rem;line-height:1.8;color:var(--ink);margin:0 0 16px}
.pd-scope{border-top:2px solid var(--navy);padding-top:clamp(16px,2vw,22px);margin-bottom:clamp(24px,3vw,34px)}
.pd-scope ul{list-style:none;margin:0;padding:0}
.pd-scope li{
  font-family:var(--body);font-weight:500;font-size:.96rem;line-height:1.6;color:var(--ink);
  padding:11px 0 11px 24px;border-bottom:1px solid var(--line);position:relative;
}
.pd-scope li::before{content:"";position:absolute;left:0;top:19px;width:9px;height:1.5px;background:var(--lblue)}

.pd-enquire{align-self:flex-start}

@media (max-width:1080px){
  .pd-viewer{aspect-ratio:16/9}
}
@media (max-width:560px){
  .pd-thumb{width:64px}
}

/* Gallery, fits the photos that actually exist.
   Two rules drive it:
     1. a frame is never wider than the photo's own pixel width (--nw), so an
        image is never upscaled into a blurry mess;
     2. the layout mode follows the count, one / row / grid, so a single
        photo reads as a deliberate centred plate rather than a broken grid.
   Adding more <figure> elements is all that's needed as photos arrive. */
.pd-gallery-wrap{padding-bottom:clamp(10px,2vw,20px)}
.pd-gallery{
  display:flex;flex-wrap:wrap;justify-content:center;align-items:flex-start;
  gap:clamp(16px,2.4vw,28px);
}
.pd-gallery .pd-figure{
  flex:0 1 auto;
  /* never exceed the photo's intrinsic width; --nw is written per figure */
  width:min(100%,var(--nw,100%));
  aspect-ratio:auto;height:auto;
}
.pd-gallery .pd-figure img{width:100%;height:auto;object-fit:contain}

/* Aspect-driven spans. These photos are mostly panoramic page captures, in a
   3-column grid a 2.5:1 shot renders ~140px tall and its HMI screens become
   unreadable. So shape decides the span, not the photo count:
     >= 2:1  -> full row (flex-wrap forces its own line)
     <  2:1  -> pairs up two-across
   Both are still capped at --nw, so nothing is ever upscaled. */
.pd-gallery--auto .pd-fig--wide{width:min(100%,var(--nw,100%))}
.pd-gallery--auto .pd-fig--half{width:min(calc(50% - 14px),var(--nw,100%))}
@media (max-width:720px){
  .pd-gallery--auto .pd-fig--half{width:min(100%,var(--nw,100%))}
}

/* Ship -> Switchboard pair, per "left (Ship) to right (Switchboard)".
   Dormant until the client supplies the marine photo pairs - the markup
   for it is generated only when that project has 2+ photos. */
.pd-pair{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:clamp(12px,2vw,24px)}
.pd-pair .pd-figure{margin:0}
.pd-pair-ar{position:relative;width:clamp(28px,4vw,52px);height:2px;background:var(--lblue);flex:0 0 auto}
.pd-pair-ar::after{
  content:"";position:absolute;right:-1px;top:50%;width:10px;height:10px;
  border-top:2px solid var(--lblue);border-right:2px solid var(--lblue);
  transform:translateY(-50%) rotate(45deg);
}

.pd-figure{margin:0;position:relative;overflow:hidden;background:var(--surface);border:1px solid var(--line);aspect-ratio:16/10;cursor:zoom-in;transition:box-shadow .35s var(--ease),transform .35s var(--ease),border-color .35s}
.pd-figure img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .55s var(--ease)}
.pd-figure:hover{transform:translateY(-4px);border-color:var(--lblue);box-shadow:0 22px 54px rgba(51,72,92,.16)}
.pd-figure:hover img{transform:scale(1.06)}

/* name + description left, scope list right */
.pd-detail-sec{padding-top:clamp(34px,4.4vw,60px)}
.pd-detail{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,4fr);gap:clamp(32px,5vw,80px);align-items:start}
.pd-k{display:flex;align-items:center;gap:12px;font-family:var(--body);font-weight:600;font-size:.78rem;letter-spacing:.2em;text-transform:uppercase;color:var(--navy);margin-bottom:16px}
.pd-k::before{content:"";width:22px;height:1.5px;background:var(--lblue);flex:0 0 auto}
.pd-copy h2{font-family:var(--head);font-weight:700;font-size:clamp(1.5rem,2.6vw,2.15rem);letter-spacing:-.02em;line-height:1.14;color:var(--navy);margin-bottom:clamp(18px,2.4vw,26px)}
.pd-copy p{font-family:var(--body);font-weight:400;font-size:1.04rem;line-height:1.85;color:var(--ink);margin:0 0 20px;max-width:60ch}
/* the client's own project points, was a screenshot, now real text */
.pd-points{list-style:none;margin:0;padding:0;max-width:64ch}
.pd-points li{
  font-family:var(--body);font-weight:400;font-size:1rem;line-height:1.72;
  color:var(--ink);padding:0 0 13px 24px;position:relative;
}
.pd-points li::before{
  content:"";position:absolute;left:0;top:.72em;width:11px;height:1.5px;background:var(--lblue);
}
.pd-specs{border-top:2px solid var(--navy);padding-top:clamp(18px,2.2vw,24px)}
.pd-specs ul{list-style:none;margin:0;padding:0}
.pd-specs li{font-family:var(--body);font-weight:500;font-size:.98rem;line-height:1.6;color:var(--ink);padding:13px 0 13px 24px;border-bottom:1px solid var(--line);position:relative}
.pd-specs li::before{content:"";position:absolute;left:0;top:21px;width:9px;height:1.5px;background:var(--lblue)}

/* project index, jump straight to any project */
.pd-index{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(12px,1.6vw,20px)}
.pd-ix{display:flex;flex-direction:column;gap:10px;background:#fff;border:1px solid var(--line);padding:10px;transition:border-color .3s,box-shadow .3s,transform .3s var(--ease)}
.pd-ix:hover{border-color:var(--lblue);box-shadow:0 12px 30px rgba(51,72,92,.1);transform:translateY(-3px)}
.pd-ix-img{display:block;aspect-ratio:16/10;overflow:hidden;background:var(--surface)}
.pd-ix-img img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .5s var(--ease)}
.pd-ix:hover .pd-ix-img img{transform:scale(1.06)}
.pd-ix-nm{font-family:var(--body);font-weight:600;font-size:.86rem;line-height:1.35;color:var(--navy);padding:0 2px 4px}
.pd-ix.is-current{border-color:var(--navy);background:var(--navy)}
.pd-ix.is-current .pd-ix-nm{color:#fff}
.pd-ix.is-current:hover{transform:none;box-shadow:none;border-color:var(--navy)}

/* prev / next */
.pd-nav{max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gut);display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--line);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.pd-nav-item{display:flex;flex-direction:column;gap:8px;background:#fff;padding:clamp(22px,3vw,38px) clamp(18px,2.6vw,34px);transition:background .3s}
.pd-nav-item:hover{background:var(--surface)}
.pd-nav-item.next{text-align:right;align-items:flex-end}
.pd-nav-k{display:flex;align-items:center;gap:10px;font-family:var(--body);font-weight:600;font-size:.76rem;letter-spacing:.18em;text-transform:uppercase;color:var(--muted)}
.pd-nav-t{font-family:var(--head);font-weight:700;font-size:clamp(1.05rem,1.9vw,1.5rem);letter-spacing:-.02em;line-height:1.2;color:var(--navy)}
.pd-nav-k .ar{width:22px;height:1.5px;background:var(--lblue);flex:0 0 auto;transition:width .3s var(--ease)}
.pd-nav-item:hover .pd-nav-k .ar{width:32px}

@media (max-width:1080px){
  .pd-detail{grid-template-columns:1fr;gap:36px}
  .pd-index{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:560px){
  .pd-nav{grid-template-columns:1fr}
  .pd-nav-item.next{text-align:left;align-items:flex-start}
  .pd-index{grid-template-columns:1fr}
  /* the pair reads top-to-bottom on phones; rotate the connector to match */
  .pd-pair{grid-template-columns:1fr;justify-items:stretch}
  .pd-pair-ar{width:2px;height:clamp(22px,6vw,34px);justify-self:center}
  .pd-pair-ar::after{right:50%;top:auto;bottom:-1px;transform:translateX(50%) rotate(135deg)}
}

/* ---------- lightbox ---------- */
.lb{position:fixed;inset:0;z-index:200;display:none;place-items:center;background:rgba(20,32,44,.92);padding:clamp(20px,5vw,60px)}
.lb.is-open{display:grid}
.lb figure{margin:0;text-align:center}
.lb img{max-width:min(1080px,92vw);max-height:80vh;width:auto;margin-inline:auto;box-shadow:0 30px 80px rgba(0,0,0,.5)}
.lb figcaption{color:#fff;margin-top:18px;font-family:var(--body);font-weight:400;font-size:1.02rem}
.lb .close{position:absolute;top:clamp(14px,3vw,28px);right:clamp(14px,3vw,28px);width:46px;height:46px;color:#fff;font-size:1.8rem;line-height:1;display:grid;place-items:center;border:1px solid rgba(255,255,255,.3);background:rgba(255,255,255,.1);z-index:2}
.lb .close:hover{background:rgba(255,255,255,.22)}
/* browse controls, prev/next either side, counter beneath */
.lb-nav{
  position:absolute;top:50%;transform:translateY(-50%);z-index:2;
  width:clamp(44px,5vw,58px);height:clamp(44px,5vw,58px);display:grid;place-items:center;
  color:#fff;border:1px solid rgba(255,255,255,.3);background:rgba(255,255,255,.1);
  transition:background .22s,border-color .22s;
}
.lb-nav:hover{background:rgba(255,255,255,.24);border-color:rgba(255,255,255,.55)}
.lb-nav.prev{left:clamp(10px,3vw,34px)}
.lb-nav.next{right:clamp(10px,3vw,34px)}
.lb-nav svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:2}
.lb-nav[hidden]{display:none}
.lb-count{
  position:absolute;left:50%;transform:translateX(-50%);bottom:clamp(16px,3vw,30px);
  font-family:var(--body);font-weight:600;font-size:.82rem;letter-spacing:.16em;
  color:rgba(255,255,255,.85);
}
.lb-count[hidden]{display:none}
@media (max-width:560px){
  /* keep the arrows clear of the image on small screens */
  .lb-nav{top:auto;bottom:clamp(16px,3vw,30px);transform:none}
  .lb-nav.prev{left:16px}.lb-nav.next{right:16px}
}

/* ---------- News ---------- */
.posts{display:flex;flex-direction:column}
.post{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:clamp(24px,3.4vw,52px);align-items:center;padding:clamp(28px,3.6vw,48px) 0;border-top:1px solid var(--line)}
.post:last-child{border-bottom:1px solid var(--line)}
.post .thumb{margin:0;aspect-ratio:16/10;overflow:hidden;background:var(--navy-deep);position:relative}
.post .thumb img{width:100%;height:100%;object-fit:cover;transition:transform .5s var(--ease)}
/* feature image not yet supplied, placeholder instead of a broken image */
.thumb--pending{display:grid;place-items:center;background:var(--surface);border:1px dashed var(--line-strong)}
.thumb--pending span{font-family:var(--body);font-weight:500;font-size:.84rem;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);text-align:center;line-height:1.7}
/* superseded by md-hzoom (fx-brand.css), two transforms on one element cancel */
.post .thumb .pidx{position:absolute;top:12px;left:12px;font-family:var(--body);font-weight:700;font-size:.72rem;letter-spacing:.12em;color:#fff;background:rgba(51,72,92,.82);padding:4px 9px}
.post .pbody{display:flex;flex-direction:column}
.post .meta{display:flex;align-items:center;flex-wrap:wrap;gap:10px;font-family:var(--body);font-weight:400;font-size:.9rem;color:var(--muted);margin-bottom:14px}
.post .meta .who{display:grid;place-items:center;width:32px;height:32px;background:var(--navy);color:#fff;font-weight:600;font-size:.78rem;flex:0 0 auto}
.post .meta .dot{width:3px;height:3px;background:var(--line-strong);border-radius:50%}
.post h2{font-family:var(--head);font-weight:700;font-size:clamp(1.3rem,2.2vw,1.85rem);line-height:1.18;letter-spacing:-.02em;color:var(--navy);margin-bottom:14px}
.post p{font-family:var(--body);font-weight:400;font-size:1rem;line-height:1.7;color:var(--ink);margin:0 0 18px;max-width:56ch}
.post .pfoot{display:flex;align-items:center;gap:18px;font-family:var(--body);font-size:.88rem;color:var(--muted)}
.post .likes{display:inline-flex;align-items:center;gap:7px;cursor:pointer}
.post .likes svg{width:16px;height:16px;fill:none;stroke:#C0362C;stroke-width:1.9;transition:fill .25s}
.post .likes.on svg{fill:#C0362C}
.empty-note{margin-top:clamp(30px,4vw,48px);border:1px dashed var(--line-strong);background:var(--surface);padding:clamp(28px,3.6vw,44px);text-align:center;font-family:var(--body);font-weight:400;color:var(--muted)}

/* ---------- inner responsive ---------- */
@media (max-width:1080px){
  .intro-grid,.wwd-grid{grid-template-columns:1fr;gap:32px}
  .intro-media{min-height:240px}
  .split{grid-template-columns:1fr}
  .split.flip .split-media{order:0}
  .split-media{min-height:220px}
  .cap-grid{grid-template-columns:repeat(2,1fr)}
  .principal{grid-template-columns:1fr}
  .post{grid-template-columns:1fr;gap:20px}
  .post .thumb{aspect-ratio:16/9}
}
@media (max-width:560px){
  .cap-grid{grid-template-columns:1fr}
  .principal .item{flex-direction:column;align-items:flex-start;text-align:left}
}

/* ==========================================================================
   SERVICES PAGE, supporting sections after the approved nine-service grid.
   Deliberately quieter than that grid: no photographic cards, no lattice, so
   nothing competes with the approved section above.
   ========================================================================== */

/* --- 1 · Industry coverage: banner, then a ruled index ------------------- */
.ind-sec{padding-top:0}
.ind-band{
  position:relative;isolation:isolate;overflow:hidden;
  min-height:clamp(230px,26vw,330px);display:flex;align-items:center;
  background:var(--surface);margin-bottom:clamp(30px,3.6vw,46px);
}
.ind-media{position:absolute;inset:0;z-index:0}
.ind-media img{width:100%;height:100%;object-fit:cover;display:block}
/* Held white across the copy, falling away right, the same ramp shape the
   About band uses, because the binding case is identical: --muted at 18.4px
   needs 4.5:1 and only has 5.5:1 on plain white to begin with. */
.ind-wash{
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(90deg,
    rgba(255,255,255,.94) 0%, rgba(255,255,255,.90) 52%,
    rgba(255,255,255,.55) 80%, rgba(255,255,255,.20) 100%);
}
.ind-inner{position:relative;z-index:2;padding:clamp(26px,3.4vw,44px) var(--gut)}
.ind-band .h-title{font-size:clamp(1.55rem,2.9vw,2.25rem);margin-bottom:12px}
.ind-band .h-sub{margin:0;max-width:56ch}

/* Industry cards, in the site's blog-card language: 16:10 thumb with the
   md-hzoom hover the news page already uses, then title and description.
   Five across at full width, folding to three, two and one. */
.ind-cards{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(206px,1fr));
  gap:clamp(14px,1.8vw,22px);
}
.ind-card{display:flex;flex-direction:column;background:#fff;border:1px solid var(--line);
  transition:transform .3s var(--ease),border-color .3s,box-shadow .3s}
.ind-card:hover{transform:translateY(-4px);border-color:var(--lblue);box-shadow:0 14px 32px rgba(51,72,92,.12)}
.ind-thumb{margin:0;aspect-ratio:16/10;overflow:hidden;background:var(--surface);position:relative}
.ind-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.ind-body{display:flex;flex-direction:column;flex:1;padding:clamp(15px,1.6vw,20px)}
.ind-body h3{font-family:var(--body);font-weight:600;font-size:1rem;color:var(--navy);
  margin:0 0 9px;line-height:1.3;letter-spacing:-.005em}
.ind-body p{font-family:var(--body);font-weight:400;font-size:.85rem;line-height:1.68;color:var(--ink);margin:0}

/* --- 2 · Project applications: four points on one continuous rule -------- */
.step-sec{background:var(--surface)}
.step-sec .wrap{position:relative}
.step-rail{position:relative;margin-top:clamp(34px,4vw,54px)}

/* The ghosted control assembly sits in the empty right of the header block, not
   behind the copy. Its own left fifth is bare white ground (measured min L .982),
   so the small overlap with the lead line needs no wash, the text keeps the full
   --surface contrast. Capped headline width stops the two ever meeting. */
.step-art{
  position:absolute;pointer-events:none;
  right:calc(var(--gut) * -0.5);top:clamp(-34px,-2vw,-8px);
  width:clamp(380px,44%,600px);
  /* No mix-blend-mode: .wrap forms a stacking context, so the image would have
     had no backdrop to multiply against. The render's studio ground is baked to
     --surface in the file itself instead, which also costs nothing to composite. */
}
.step-art img{display:block;width:100%;height:auto}
.step-sec .eyebrow,.step-sec .h-title,.step-sec .h-sub{position:relative;z-index:1}
.step-sec .h-title,.step-sec .h-sub{max-width:640px}
@media (max-width:1080px){.step-art{display:none}}

/* The flow scene. flow.css ships in SyncTech red, so every stroke and the pop
   glow are remapped to the brand here, light blue for the live current, navy
   for the nodes, the hairline for the resting wire. */
.step-scene{margin-bottom:clamp(18px,2.2vw,26px)}
.step-scene svg{overflow:visible}
/* Three-class selectors, deliberately: fx.css declares `.scene .eflow` at the
   same specificity but loads later, so a two-class override loses and the
   current renders in SyncTech red at flow.css's own 1.6s dash. */
.step-sec .step-scene .trace{fill:none;stroke:var(--line-strong);stroke-width:2;stroke-linecap:round}
.step-sec .step-scene .eflow{
  stroke:var(--lblue);stroke-width:3;
  stroke-dasharray:70 1090;stroke-dashoffset:70;
  animation:md-flow 5.5s linear infinite;
}
.step-scene .nd{--pop:rgba(160,201,238,.75)}
.step-scene .nd-halo{fill:var(--lblue);opacity:.20}
.step-scene .nd-core{fill:var(--navy)}
.step-scene .nd-pulse{fill:var(--lblue)}

.step-items{display:grid;grid-template-columns:repeat(4,1fr);gap:0}
/* gap taken as padding so each column is exactly a quarter, that is what keeps
   the SVG node positions locked to the headings at every width */
.step{padding-right:clamp(20px,3vw,44px)}
.step h3{
  font-family:var(--body);font-weight:600;font-size:1.02rem;color:var(--navy);
  margin:0 0 10px;line-height:1.3;letter-spacing:-.005em;
}
.step p{font-family:var(--body);font-weight:400;font-size:.9rem;line-height:1.72;color:var(--ink);margin:0}

@media (max-width:820px){
  /* the scene is a horizontal band by design; on one column it would be a
     meaningless 1152-wide line, so it is dropped and the items stack plainly */
  .step-scene{display:none}
  .step-items{grid-template-columns:1fr;gap:0}
  .step{padding:0 0 26px;border-left:2px solid var(--line);padding-left:22px;position:relative}
  .step:last-child{padding-bottom:0}
  .step::before{
    content:"";position:absolute;left:-6px;top:5px;width:10px;height:10px;
    border-radius:50%;background:var(--navy);
  }
}

/* --- 3 · System coverage: two-column technical schedule ------------------ */
.sys-sched{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  column-gap:clamp(30px,4.6vw,72px);margin:clamp(30px,3.6vw,46px) 0 0;
  border-top:1px solid var(--line);
}
.sys-row{padding:clamp(15px,1.8vw,20px) 0;border-bottom:1px solid var(--line)}
.sys-sched dt{
  font-family:var(--body);font-weight:600;font-size:.98rem;color:var(--navy);
  margin:0 0 6px;letter-spacing:-.005em;
}
.sys-sched dd{
  font-family:var(--body);font-weight:400;font-size:.89rem;line-height:1.7;
  color:var(--ink);margin:0;
}
@media (max-width:760px){.sys-sched{grid-template-columns:1fr}}

/* --- 4 · Project evidence: three snapshots ------------------------------- */
.ev-sec{background:var(--surface)}
.ev-grid{
  display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(16px,2vw,26px);
  margin:clamp(30px,3.6vw,46px) 0 clamp(28px,3.2vw,40px);
}
.ev-card{
  display:flex;flex-direction:column;background:#fff;border:1px solid var(--line);
  transition:transform .3s var(--ease),box-shadow .3s,border-color .3s;
}
.ev-card:hover{transform:translateY(-4px);border-color:var(--lblue);box-shadow:0 16px 38px rgba(51,72,92,.14)}
.ev-media{display:block;aspect-ratio:4/3;overflow:hidden;background:var(--surface)}
.ev-media img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .5s var(--ease)}
.ev-card:hover .ev-media img{transform:scale(1.04)}
.ev-body{display:flex;flex-direction:column;flex:1;padding:clamp(18px,2vw,24px)}
.ev-h{
  font-family:var(--body);font-weight:600;font-size:1.02rem;color:var(--navy);
  line-height:1.3;margin-bottom:9px;letter-spacing:-.005em;
}
.ev-p{font-family:var(--body);font-weight:400;font-size:.87rem;line-height:1.7;color:var(--ink)}
.ev-go{
  margin-top:auto;padding-top:16px;display:flex;align-items:center;gap:9px;
  font-family:var(--body);font-weight:600;font-size:.7rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--navy);
}
.ev-card:hover .ev-go{color:var(--navy-deep)}
.ev-all{align-self:flex-start}
@media (max-width:900px){.ev-grid{grid-template-columns:1fr 1fr}}
@media (max-width:620px){.ev-grid{grid-template-columns:1fr}}

/* --- 5 · Project enquiry panel ------------------------------------------ */
.enq-panel{
  display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);
  gap:clamp(28px,4vw,62px);align-items:start;
  border:1px solid var(--line);padding:clamp(28px,3.6vw,52px);background:#fff;
}
.enq-copy .h-title{font-size:clamp(1.5rem,2.6vw,2.05rem);margin:0 0 14px}
.enq-copy p{font-family:var(--body);font-weight:400;font-size:1rem;line-height:1.8;color:var(--ink);margin:0 0 26px;max-width:52ch}
.enq-acts{display:flex;flex-wrap:wrap;align-items:center;gap:16px 22px}
/* padding, not just a baseline rule, a bare inline link measured 221x31 and
   WCAG wants a 44px target */
.enq-link{
  display:inline-flex;align-items:center;min-height:44px;
  font-family:var(--body);font-weight:600;font-size:.92rem;color:var(--navy);
  box-shadow:inset 0 -1px 0 var(--line-strong);transition:color .24s,box-shadow .24s;
}
.enq-link:hover{color:var(--navy-deep);box-shadow:inset 0 -1px 0 var(--lblue)}
.enq-aside h3{
  font-family:var(--body);font-weight:600;font-size:.7rem;letter-spacing:.18em;
  text-transform:uppercase;color:var(--muted);margin:0 0 16px;
}
.enq-list{list-style:none;margin:0;padding:0}
.enq-list li{
  position:relative;padding:11px 0 11px 26px;border-bottom:1px solid var(--line);
  font-family:var(--body);font-weight:400;font-size:.92rem;line-height:1.6;color:var(--ink);
}
.enq-list li:first-child{border-top:1px solid var(--line)}
/* square tick mark rather than an icon font */
.enq-list li::before{
  content:"";position:absolute;left:2px;top:17px;width:9px;height:5px;
  border-left:2px solid var(--lblue);border-bottom:2px solid var(--lblue);
  transform:rotate(-45deg);
}
@media (max-width:820px){.enq-panel{grid-template-columns:1fr;gap:30px}}

/* Motion nodes on the four intervention points.
   md-ring supplies the emanating pulse (its --ring-c is already remapped to the
   brand light blue in fx-brand.css); the dots are square, so the rings read as
   expanding technical brackets rather than sonar circles. Staggering by --d
   makes the pulse travel along the rail rather than firing all four at once.
   The travelling segment below is gated on the governor's full tier AND on
   reduced-motion, so it stops dead on lite/off and for anyone who asks. */
@media (prefers-reduced-motion:no-preference){
  :root[data-motion="full"] .step-rail{position:relative}
  :root[data-motion="full"] .step-rail::after{
    content:"";position:absolute;top:5px;left:0;width:70px;height:1px;z-index:0;
    background:linear-gradient(90deg,transparent,var(--lblue),transparent);
    animation:step-run 7s linear infinite;
  }
  @keyframes step-run{
    0%{transform:translateX(0);opacity:0}
    8%{opacity:1}
    82%{opacity:1}
    100%{transform:translateX(calc(100% + 1152px));opacity:0}
  }
  @media (max-width:820px){
    /* rail is vertical here, run the segment down it instead */
    :root[data-motion="full"] .step-rail::after{
      top:0;left:5px;width:1px;height:70px;
      background:linear-gradient(180deg,transparent,var(--lblue),transparent);
      animation:step-run-y 7s linear infinite;
    }
    @keyframes step-run-y{
      0%{transform:translateY(0);opacity:0}
      8%{opacity:1}
      82%{opacity:1}
      100%{transform:translateY(calc(100% + 600px));opacity:0}
    }
  }
}

/* --- effect brand-mapping for the new services sections ------------------ */
/* md-shine ships as a white sweep tuned for dark cards; on these light cards
   it needs the light-blue tint the client rail already uses. */
.ind-card.md-shine::after,.ev-card.md-shine::after,
.proj.md-shine::after,.pd-ix.md-shine::after,.cap.md-shine::after{
  background:linear-gradient(105deg,transparent 38%,rgba(160,201,238,.30) 50%,transparent 62%);
}
/* md-corners draws its brackets at the element edge; the evidence cards have a
   1px border, so inset them or they sit on top of it */
.ev-card.md-corners::before,.ev-card.md-corners::after{top:10px;left:10px}
/* md-words sets its own line-height; keep the section headings on ours */
.svc-sec .md-words,.ind-band .md-words,.step-sec .md-words,
.sys-sec .md-words,.ev-sec .md-words,.enq-copy .md-words,
h2.h-title.md-words,.contact-body .md-words{line-height:1.12}


/* ---------- Projects hero: the two CTAs drawn as IC packages ----------------
   Scoped to .hero--ic so the approved button on the other nine pages is
   untouched. Both packages are the SAME width because the grid gives them
   equal tracks - not because the padding was tuned to each label, which would
   drift the moment the wording changed. Equal width also means the pin rows,
   generated by a repeating gradient, come out with the same pin count. */
.hero--ic .hero-acts{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
  gap:26px;max-width:700px;margin-top:40px}
.hero--ic .hero-cta{
  position:relative;justify-content:center;text-align:center;align-self:stretch;
  /* The longer label is 29 characters; at the previous 1rem/.03em it overran
     the equal track and .md-swap clipped it mid-word. Sized so the longest
     label fits the track rather than trimming the wording. */
  padding:17px 20px;border-radius:5px;letter-spacing:.012em;font-size:.94rem;
  /* a sheen that sweeps on hover, reading as current entering the package */
  background-image:linear-gradient(104deg,transparent 42%,rgba(255,255,255,.42) 50%,transparent 58%);
  background-size:240% 100%;background-position:190% 0;background-repeat:no-repeat;
  transition:background-color .26s,transform .26s var(--ease),box-shadow .26s,
             gap .26s var(--ease),background-position .8s ease}
.hero--ic .hero-cta:hover{gap:16px;background-position:-70% 0}

/* pin rows above and below the package */
.hero--ic .hero-cta::before,
.hero--ic .hero-cta::after{
  content:"";position:absolute;left:15px;right:15px;height:6px;pointer-events:none;
  background:repeating-linear-gradient(90deg,var(--lblue) 0 3px,transparent 3px 14px);
  opacity:.6;transition:opacity .26s}
.hero--ic .hero-cta::before{top:-6px}
.hero--ic .hero-cta::after{bottom:-6px}
.hero--ic .hero-cta:hover::before,
.hero--ic .hero-cta:hover::after{opacity:1}

/* pin-1 mark, same convention as the packages on the board behind */
.hero--ic .ic-dot{position:absolute;left:11px;top:10px;width:5px;height:5px;
  border-radius:50%;background:var(--navy-deep);opacity:.5}

/* The second button was marked .ghost in the markup but no rule ever defined
   it, so both rendered as the same filled chip. It carries its own translucent
   ground rather than sitting straight on the board, so its contrast does not
   depend on whichever trace happens to run behind it. */
.hero--ic .hero-cta.ghost{
  background-color:rgba(34,49,63,.55);color:var(--on-navy-soft);
  box-shadow:inset 0 0 0 1.5px var(--lblue)}
.hero--ic .hero-cta.ghost:hover{background-color:rgba(34,49,63,.34);
  box-shadow:inset 0 0 0 1.5px var(--lblue),0 12px 28px rgba(160,201,238,.22)}
.hero--ic .ghost .ic-dot{background:var(--lblue);opacity:.85}
.hero--ic .ghost .arw::before{background:var(--lblue)}
.hero--ic .ghost .arw::after{border-top-color:var(--lblue);border-right-color:var(--lblue)}

@media (prefers-reduced-motion:reduce){
  .hero--ic .hero-cta{transition:background-color .26s,box-shadow .26s}
  .hero--ic .hero-cta:hover{transform:none;background-position:190% 0}
}


/* ---------- Homepage service cards ----------------------------------------
   index.html and services.html both use .svc but with different anatomies.
   On the services page the copy lives inside .svc-body, which carries all the
   padding, and there is no icon. On the homepage the card holds .svc-ico, h3
   and p as direct children - so they inherited no padding and ran flush to the
   card edge, and .svc-ico had no rule anywhere in the stylesheet. Its SVG
   declares only a viewBox, so with no width it fell back to filling the card
   and scaled the 64-unit artwork to ~370px, which is what stretched every card.
   Scoped to #services (index.html only) so the services page card is untouched. */
#services .svc{padding-bottom:clamp(20px,2.2vw,26px)}
#services .svc-ico{
  width:56px;height:56px;flex:0 0 auto;
  margin:clamp(20px,2.2vw,26px) clamp(20px,2.2vw,26px) 0}
/* The icons stroke with url(#gg), a navy-to-lblue gradient whose navy end all
   but disappears on the navy-deep card. Overriding stroke here also makes the
   nine duplicate id="gg" gradients irrelevant to rendering - every card was
   resolving to the first one regardless. */
#services .svc-ico svg{width:100%;height:100%;display:block;stroke:var(--lblue);opacity:.9}
#services .svc>h3{margin:16px clamp(20px,2.2vw,26px) 10px}
#services .svc>p{margin:0 clamp(20px,2.2vw,26px)}


/* ---------- Projects: make the case-study link read as a control -----------
   It was an uppercase text label with a 26px hairline - almost no affordance,
   and readers were missing it. Two changes: the link becomes a filled button,
   and the photograph becomes a click target for the same destination.
   The photo target is a stretched pseudo-element on the SAME anchor rather
   than a second <a>, so the accessibility tree still holds exactly one link
   per row. .pj-copy is lifted above it so the description stays selectable
   and only the image column is covered. */
.pj-row{position:relative}
.pj-copy{position:relative;z-index:2}
.pj-link::after{content:"";position:absolute;inset:0;z-index:1}
.pj-shot{cursor:pointer}

.pj-link{
  background:var(--navy);color:#fff;
  padding:13px 22px;border-radius:var(--r-sm);min-height:44px;
  font-size:.74rem;letter-spacing:.13em;gap:12px;
  transition:background .24s,gap .24s var(--ease),
             box-shadow .24s,transform .24s var(--ease)}
.pj-link:hover{
  background:var(--navy-deep);gap:18px;transform:translateY(-1px);
  box-shadow:0 10px 24px rgba(51,72,92,.22)}
.pj-link .ar{position:relative;width:22px;background:#fff}
.pj-link .ar::after{
  content:"";position:absolute;right:0;top:50%;width:7px;height:7px;
  border-top:1.5px solid #fff;border-right:1.5px solid #fff;
  transform:translateY(-50%) rotate(45deg)}
.pj-link:hover .ar{width:32px}

/* the image already zooms on row hover; now that it is a target, say so */
.pj-row:hover .pj-shot img{transform:scale(1.045)}

@media (prefers-reduced-motion:reduce){
  .pj-link{transition:background .24s}
  .pj-link:hover{transform:none}
}


/* ---------- Project detail: make the photo viewer actually fit -------------
   .pd-viewer is display:grid + place-items:center, which sizes the grid AREA
   to its content. A percentage width/height on the image therefore resolves
   against an indefinite area and falls back to auto, so every photo rendered
   at its intrinsic pixel size, overflowed the box and was clipped by
   overflow:hidden - the middle band of the frame, subject cut off. object-fit
   never engaged at all, which is why forcing `contain` changed nothing.
   Absolute positioning gives the image a definite box, so object-fit applies.

   Stage ratio chosen from the real set, not by eye: 41 photos across the eight
   pages run 0.75 (portrait) to 3.00 (panoramic). Measured mean coverage of the
   stage - 21:8 51%, 16:9 62%, 3:2 69%, 4:3 75% - and worst-case coverage,
   where 3:2 is best at 50% against 29% today. 4:3 edges it on the mean but
   needs an 864px-tall stage. So 3:2, capped so it cannot dominate a short
   window. `contain` because these are documentation photographs: cropping a
   3:4 portrait to a wide stage throws away most of the evidence. */
/* The photograph is matted inside the frame rather than butting against it.
   Padding on .pd-viewer would not do this: an absolutely positioned child
   resolves inset against the PADDING box, so it would sit on top of the
   padding. The gap has to come from the inset value itself. */
/* The photograph is matted inside the frame rather than butting against it.
   Two things that do NOT work here: padding on .pd-viewer (an absolutely
   positioned child resolves inset against the PADDING box, so it sits on top
   of the padding), and inset:<gap> with width/height auto (a replaced element
   with auto sizing falls back to its intrinsic size and overflows again).
   Padding on the image itself is the lever - object-fit fits the photo inside
   the content box, so the padding becomes an even mount on all four sides. */
.pd-viewer img{
  position:absolute;inset:0;width:100%;height:100%;
  padding:clamp(12px,1.5vw,22px);box-sizing:border-box;object-fit:contain}
.pd-hero .pd-viewer{aspect-ratio:3/2;max-height:min(74vh,720px)}
.pd-thumb{position:relative}
.pd-thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}


/* The contact hero's eyebrow is the board's own etched copper, lifted out of
   the PCB and set above the headline. Sized here; the wordmark SVG carries its
   own scoped styling and scales its stroke weight with whatever height it gets,
   so it stays crisp from 23px up. */
.hero-body .md-etch{height:clamp(28px,3.1vw,42px);margin:0 0 clamp(20px,2.4vw,28px)}




/* ---------- News: listing + article ------------------------------------
   Single column with a featured post on top and a category filter above it.
   The image box takes each photograph's OWN ratio (--ar), so nothing is
   cropped and nothing is letterboxed - a fixed box can only ever do one or the
   other when the sources differ, and these run 1.33 and 2.57. */
.nf-sec{padding-top:clamp(16px,2.2vw,28px)}
.nf-filter{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:clamp(26px,3.4vw,40px);
  padding-bottom:clamp(18px,2.4vw,26px);border-bottom:1px solid var(--line)}
.nf-chip{font-family:var(--body);font-weight:600;font-size:.82rem;letter-spacing:.02em;
  color:var(--ink);background:#fff;border:1px solid var(--line-strong);
  border-radius:999px;padding:8px 15px;min-height:38px;cursor:pointer;
  transition:background .2s,border-color .2s,color .2s}
.nf-chip .n{color:var(--muted);font-weight:500;margin-left:3px}
.nf-chip:hover{border-color:var(--navy)}
.nf-chip.is-on{background:var(--navy);border-color:var(--navy);color:#fff}
.nf-chip.is-on .n{color:var(--lblue)}

.nf-meta{display:flex;align-items:center;flex-wrap:wrap;gap:9px;
  font-family:var(--body);font-size:.78rem;color:var(--muted);margin-bottom:12px}
.nf-meta .dot{width:3px;height:3px;border-radius:50%;background:var(--line-strong);flex:0 0 auto}
.nf-cat{font-weight:700;font-size:.68rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--navy);background:var(--lblue-tint);padding:4px 9px;border-radius:2px}

.nf-feat{display:grid;grid-template-columns:1.15fr 1fr;gap:clamp(24px,3.4vw,48px);
  align-items:center;padding-bottom:clamp(30px,4vw,46px);margin-bottom:clamp(26px,3.4vw,40px);
  border-bottom:1px solid var(--line)}
.nf-feat-media,.nf-row-media{display:block;aspect-ratio:var(--ar);overflow:hidden;
  background:var(--surface);border:1px solid var(--line)}
.nf-feat-media img,.nf-row-media img{width:100%;height:auto;display:block}
.nf-feat h2{font-family:var(--head);font-weight:700;letter-spacing:-.02em;line-height:1.18;
  font-size:clamp(1.4rem,2.3vw,2rem);margin:0 0 14px;text-wrap:balance}
.nf-feat h2 a,.nf-row h3 a{color:var(--navy);text-decoration:none}
.nf-feat h2 a:hover,.nf-row h3 a:hover{color:var(--navy-deep)}
.nf-feat p,.nf-row p{font-size:clamp(.95rem,1.1vw,1.02rem);line-height:1.75;color:var(--ink);margin:0 0 18px}

.nf-list{display:flex;flex-direction:column}
.nf-row{display:grid;grid-template-columns:230px 1fr;gap:clamp(20px,2.6vw,34px);
  align-items:start;padding:clamp(22px,2.8vw,32px) 0;border-bottom:1px solid var(--line)}
.nf-row:has(.nf-row-body:only-child){grid-template-columns:1fr}
.nf-row h3{font-family:var(--head);font-weight:700;letter-spacing:-.015em;line-height:1.24;
  font-size:clamp(1.1rem,1.5vw,1.32rem);margin:0 0 10px;text-wrap:balance}

.nf-read{display:inline-flex;align-items:center;gap:10px;min-height:32px;
  font-family:var(--body);font-weight:600;font-size:.76rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--navy)}
.nf-read .ar{width:22px;height:1.5px;background:var(--lblue);position:relative;transition:width .26s var(--ease)}
.nf-read .ar::after{content:"";position:absolute;right:0;top:50%;width:6px;height:6px;
  border-top:1.5px solid var(--lblue);border-right:1.5px solid var(--lblue);
  transform:translateY(-50%) rotate(45deg)}
.nf-read:hover .ar{width:34px}
.nf-empty{color:var(--muted);padding:30px 0}

/* article page */
.na-art{max-width:820px;margin:0 auto}
.na-head{margin:clamp(18px,2.4vw,30px) 0 clamp(20px,2.6vw,30px)}
.na-head h1{font-family:var(--head);font-weight:700;letter-spacing:-.022em;line-height:1.14;
  font-size:clamp(1.7rem,3.4vw,2.6rem);color:var(--navy);margin:0 0 16px;text-wrap:balance}
.na-stand{font-size:clamp(1rem,1.25vw,1.12rem);line-height:1.72;color:var(--ink);margin:0 0 20px}
.na-by{display:flex;align-items:center;gap:10px;font-size:.84rem;color:var(--muted)}
.na-by .who{display:grid;place-items:center;width:30px;height:30px;border-radius:50%;
  background:var(--navy);color:#fff;font-size:.68rem;font-weight:700;letter-spacing:.04em}
.na-fig{margin:0 0 clamp(22px,3vw,34px);aspect-ratio:var(--ar);overflow:hidden;
  background:var(--surface);border:1px solid var(--line)}
.na-fig img{width:100%;height:auto;display:block}
.na-body{font-size:1.02rem;line-height:1.8;color:var(--ink)}
.na-body p{margin:0 0 18px}
.na-nav{display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;
  margin-top:clamp(30px,4vw,48px);padding-top:clamp(22px,3vw,30px);border-top:1px solid var(--line)}
.na-nav a{flex:1 1 260px;color:var(--navy);font-weight:600;line-height:1.4;font-size:.96rem}
.na-nav a span{display:block;font-size:.68rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);font-weight:700;margin-bottom:5px}
.na-nav .na-next{text-align:right}
.na-nav a:hover{color:var(--navy-deep)}

@media (max-width:860px){
  .nf-feat{grid-template-columns:1fr}
  .nf-row{grid-template-columns:1fr}
  .nf-row-media{max-width:420px}
}


/* ---------- Hero entrance: pan (all pages) -------------------------------
   Canva's "Pan, on enter, medium, direction right": the copy slides in from
   the left as it fades. Applies to every .hero on the site, inside .hero-body only,
   so the stats strip below keeps the standard rise.

   An ANIMATION, not the usual .reveal transition. .reveal is driven by an
   IntersectionObserver, and for content above the fold that observer fires in
   the same frame the page first paints - before the start state has been
   committed - so the browser skips the transition and snaps to the end. That
   is why the hero title appeared to have no entrance at all. A keyframe
   animation runs on load regardless of when any class arrives.

   Delays mirror the .reveal[data-d] stagger so the eyebrow, headline, lead and
   button still arrive 0.08s apart. Travel is 96px and the copy starts 144px in,
   so nothing crosses the viewport edge. */
@keyframes heroPan{from{opacity:0;transform:translateX(-96px)}to{opacity:1;transform:none}}
.hero .hero-body .reveal{
  opacity:0;transform:translateX(-96px);transition:none;
  /* Held until the page is ready. A CSS animation starts when the element is
     first styled - at parse time - but this hero carries a large photograph,
     and by the time that decodes and the page looks ready the 0.9s has already
     run. That is why it appeared to do nothing on the homepage while the same
     technique reads fine on the board pages. v3.js releases it once the page is up. */
  animation-play-state:paused;
  /* NOT --ease-out: that curve is cubic-bezier(.16,1,.3,1) and covers 75% of
     the travel in the first fifth of the duration, so the slide reads as a
     flick. This one spreads the movement across the whole 0.9s. */
  animation:heroPan 1.35s cubic-bezier(.25,.46,.45,.94) both}
/* Each line gets its own pace, not just its own delay. The lead is the slowest
   by a clear margin, so it drifts in under the headline instead of tracking it.
   Longhands only - the shorthand up top already set the curve and fill. */
.hero .hero-body .reveal{animation-duration:1.15s}                      /* eyebrow */
.hero .hero-body .reveal[data-d="1"]{animation-delay:.13s;animation-duration:1.35s}  /* headline */
.hero .hero-body .reveal[data-d="2"]{animation-delay:.30s;animation-duration:2.1s}   /* lead - slowest */
.hero .hero-body .reveal[data-d="3"]{animation-delay:.52s;animation-duration:1.25s}  /* button */
.hero .hero-body .reveal.in{opacity:1}
html.hero-ready .hero .hero-body .reveal{animation-play-state:running}
/* never let it stay hidden if the release never arrives */
html.no-js .hero .hero-body .reveal{animation:none;opacity:1;transform:none}

@media (prefers-reduced-motion:reduce){
  .hero .hero-body .reveal{animation:none;opacity:1;transform:none}
}


/* ---------- Slower section + component entrances, site-wide -------------
   Was scoped to body.home; now every page shares the one entrance so the
   rhythm does not change as you move between pages.
   Longer travel as well as longer duration - at 1.4s a 22px rise reads as a
   fade that happens to drift, so the distance goes up with the time.
   Hero copy is not touched here; it runs its own pan animation, and its
   .hero .hero-body .reveal rules outrank these on specificity. */
.reveal{
  transform:translateY(38px);
  transition-duration:1.4s;
  transition-timing-function:cubic-bezier(.25,.46,.45,.94)}
.reveal.in{transform:none}
/* stagger widened to match - the old .08s steps bunch up at this duration */
.reveal[data-d="1"]{transition-delay:.16s}
.reveal[data-d="2"]{transition-delay:.32s}
.reveal[data-d="3"]{transition-delay:.48s}
.reveal[data-d="4"]{transition-delay:.64s}
.reveal[data-d="5"]{transition-delay:.80s}
.reveal[data-d="6"]{transition-delay:.96s}

@media (prefers-reduced-motion:reduce){
  .reveal{transform:none;transition-duration:.01ms}
}


/* ---------- News article body ------------------------------------------ */
.na-body h2{font-family:var(--head);font-weight:700;letter-spacing:-.018em;line-height:1.22;
  font-size:clamp(1.3rem,2vw,1.62rem);color:var(--navy);margin:clamp(34px,4vw,48px) 0 14px;text-wrap:balance}
.na-body h3{font-family:var(--body);font-weight:700;font-size:clamp(1.02rem,1.3vw,1.14rem);
  color:var(--navy);margin:clamp(26px,3vw,34px) 0 10px}
.na-body .na-list{margin:0 0 18px;padding-left:22px}
.na-body .na-list li{margin-bottom:12px;line-height:1.78}
.na-body .na-list.na-tight li{margin-bottom:5px}
.na-inline{margin:clamp(18px,2.4vw,26px) 0;aspect-ratio:var(--ar);overflow:hidden;
  background:var(--surface);border:1px solid var(--line)}
.na-inline img{width:100%;height:auto;display:block}
.na-inline figcaption{font-size:.82rem;color:var(--muted);padding:9px 12px;background:#fff;
  border-top:1px solid var(--line)}
.na-inline:has(figcaption){aspect-ratio:auto}
.na-tablewrap{overflow-x:auto;margin:clamp(22px,3vw,30px) 0;border:1px solid var(--line)}
.na-table{border-collapse:collapse;width:100%;min-width:720px;font-size:.84rem}
.na-table caption{text-align:left;font-size:.78rem;color:var(--muted);padding:10px 12px;
  border-bottom:1px solid var(--line)}
.na-table th,.na-table td{border-bottom:1px solid var(--line);padding:9px 12px;
  vertical-align:top;line-height:1.5}
.na-table thead th{background:var(--navy);color:#fff;font-weight:700;font-size:.8rem;
  letter-spacing:.04em;position:sticky;top:0}
.na-table tbody th{background:var(--surface);color:var(--navy);font-weight:700;text-align:left;
  white-space:nowrap}
.na-table tbody tr:last-child th,.na-table tbody tr:last-child td{border-bottom:0;font-weight:700}
.na-cta{margin:clamp(28px,3.4vw,38px) 0 0;padding:clamp(18px,2.2vw,26px);
  background:var(--lblue-tint);border-radius:3px}
.na-cta p{font-family:var(--head);font-weight:700;font-size:clamp(1.05rem,1.5vw,1.22rem);
  line-height:1.4;color:var(--navy);margin:0 0 16px}
.na-note{font-size:.82rem;color:var(--muted);margin-top:18px}


/* ---------- Article downloads ------------------------------------------ */
.na-docs{list-style:none;margin:0 0 8px;padding:0;display:grid;gap:10px}
.na-docs a{display:flex;align-items:center;gap:14px;padding:14px 16px;
  border:1px solid var(--line);border-radius:3px;background:#fff;
  transition:border-color .22s,box-shadow .22s,transform .22s var(--ease)}
.na-docs a:hover{border-color:var(--navy);box-shadow:0 8px 20px rgba(51,72,92,.10);transform:translateY(-1px)}
.na-docs .d-ico{flex:0 0 auto;display:grid;place-items:center;width:42px;height:42px;
  background:var(--navy);color:#fff;font-size:.62rem;font-weight:700;letter-spacing:.08em;border-radius:2px}
.na-docs .d-name{font-weight:600;color:var(--navy);line-height:1.35}
.na-docs .d-meta{display:block;font-weight:400;font-size:.8rem;color:var(--muted);margin-top:2px}


/* ---------- Contact: direct-contact row, service select, map band -------
   The page was a form beside an address block and little else. These three
   pieces give a visitor who does not want to fill in a form somewhere to go,
   tell us what the enquiry is about before it is written, and show where the
   yard actually is. Nothing here states a fact the site did not already
   carry - hours are deliberately absent, see the TODO on contact.html. */
.cmethods{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(14px,1.6vw,22px)}
/* Photo-backed cards that spin a full turn on hover.
   Structure matters: .cmethod is the hover target and is NEVER transformed,
   .cm-face is the thing that rotates. Rotating the hover target itself makes
   it edge-on at 90deg, the pointer falls outside it, hover drops, the spin
   reverses - and the card judders in a loop under the cursor. */
.cmethod{perspective:1200px;border-radius:var(--r-md)}
.cm-face{display:flex;flex-direction:column;gap:10px;padding:clamp(22px,2.6vw,30px);
  height:100%;position:relative;isolation:isolate;
  background:var(--navy-ink) center/cover no-repeat;
  border:1px solid rgba(160,201,238,.22);border-radius:var(--r-md);
  transform-style:preserve-3d;backface-visibility:hidden;
  transition:transform .9s cubic-bezier(.25,.46,.45,.94),box-shadow .3s,border-color .3s}
/* the photographs are dark but not uniformly so - the scrim is what actually
   guarantees the text contrast, and it is measured on the rendered page */
.cm-face::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;
  background:linear-gradient(180deg,rgba(34,49,63,.50) 0%,rgba(34,49,63,.78) 100%)}
.cmethod:hover .cm-face{transform:rotateY(360deg);
  border-color:var(--lblue);box-shadow:0 16px 38px rgba(20,32,44,.34)}
.cm-ico{width:26px;height:26px;color:var(--lblue)}
.cm-ico svg{width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.cm-k{font-family:var(--body);font-weight:600;font-size:.74rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--on-navy-soft)}
.cm-v{font-family:var(--body);font-weight:600;font-size:clamp(1rem,1.3vw,1.12rem);
  line-height:1.45;color:#fff;text-decoration:none;transition:color .22s}
a.cm-v:hover{color:var(--lblue)}
.cm-sub{font-family:var(--body);font-size:.92rem;line-height:1.6;color:var(--on-navy);margin:0}

.cfield select{width:100%;font-family:var(--body);font-size:1rem;color:var(--ink);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:12px 40px 12px 14px;outline:0;cursor:pointer;-webkit-appearance:none;appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235B6B7A' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 13px center;background-size:17px;
  transition:border-color .22s,background-color .22s,box-shadow .22s}
.cfield select:focus{border-color:var(--navy);background-color:#fff;
  box-shadow:0 0 0 3px rgba(78,105,129,.1)}
.creq{font-family:var(--body);font-size:.86rem;color:var(--muted);margin:0 0 22px}

.map-grid{display:grid;grid-template-columns:1.55fr 1fr;gap:0;
  border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden}
.map-frame{position:relative;aspect-ratio:16/10;background:var(--surface)}
.map-frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}
.map-side{display:flex;flex-direction:column;gap:18px;justify-content:center;
  padding:clamp(26px,3.2vw,44px);background:var(--navy-ink);color:var(--on-navy)}
.map-side h3{font-family:var(--head);font-weight:700;letter-spacing:-.015em;
  font-size:clamp(1.15rem,1.7vw,1.4rem);color:#fff;margin:0}
.map-side address{font-family:var(--body);font-style:normal;font-size:1rem;
  line-height:1.75;color:var(--on-navy-soft);margin:0}
.map-acts{display:flex;flex-wrap:wrap;gap:12px;margin-top:4px}
.map-btn{display:inline-flex;align-items:center;gap:9px;font-family:var(--body);
  font-weight:600;font-size:.94rem;text-decoration:none;padding:12px 22px;
  border-radius:var(--r-sm);border:1px solid rgba(160,201,238,.42);color:#fff;
  transition:background .24s,border-color .24s,transform .26s var(--ease)}
.map-btn svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
.map-btn:hover{background:rgba(160,201,238,.14);border-color:var(--lblue);transform:translateY(-2px)}
.map-btn.solid{background:var(--lblue);border-color:var(--lblue);color:var(--navy-ink)}
.map-btn.solid:hover{background:#fff;border-color:#fff}

@media (max-width:900px){
  .cmethods{grid-template-columns:1fr}
  .map-grid{grid-template-columns:1fr}
  .map-frame{aspect-ratio:4/3}
}

/* required-field marker and a failed-validation state for the enquiry form */
.cfield label .req{color:var(--lblue-deep,#3B6C99);font-weight:700;margin-left:2px}
.cmsg.err{color:#A3312B}
.cfield input[aria-invalid="true"],.cfield textarea[aria-invalid="true"]{
  border-color:#A3312B;box-shadow:0 0 0 3px rgba(163,49,43,.10)}

/* A full 360 is decorative motion with no informational content - it is the
   first thing to drop for anyone who has asked for less movement. */
@media (prefers-reduced-motion:reduce){
  .cm-face{transition:box-shadow .3s,border-color .3s}
  .cmethod:hover .cm-face{transform:none}
}

/* p2 (WD-2607-004): with the Petronas-vendor and SSM pills removed, the strip
   holds a single item - centre it instead of leaving it hard left. */
.cred-strip.cred-solo{justify-content:center}
.cred-strip.cred-solo .cred{flex:0 0 auto;justify-content:center}

/* p22 (WD-2607-004): with DEIF added the strip held five cards in a four-column
   grid, so Cabex sat alone at the far left of a second row. Six columns with
   each card spanning two gives 3 over 2.

   The two half-columns either side of the bottom pair must be FILLED, not left
   empty. The strip's own background is a translucent white that paints the 1px
   hairlines between cards; across an empty grid area that same translucent
   white sits over the photograph instead, so the band beside the bottom row
   read visibly lighter than the cards - the panel looked patched.

   The pseudo-elements are placed explicitly in row 2, columns 1 and 6, and
   carry the card colour, so the block is one even navy panel. Because they are
   definitely-positioned, auto-placement flows the fourth and fifth cards into
   columns 2-3 and 4-5, which is what centres them - no nth-child nudge needed.

   Dividers are drawn as BORDERS ON THE CARDS, not by the 1px grid gap. The gap
   paints a line at every boundary, including card-to-filler, so the bottom row
   picked up two stray verticals that lined up with nothing in the row above.
   Bordering the cards puts a line only where two cards actually meet: two in
   the top row, one between DEIF and Cabex, and one horizontal rule under the
   top row that runs the full width. */
@media (min-width:1081px){
  .brand-strip{grid-template-columns:repeat(6,1fr);gap:0;background:var(--navy-deep)}
  .brand-strip .brand-cell{grid-column:span 2;border-right:1px solid rgba(255,255,255,.14)}
  .brand-strip .brand-cell:nth-child(-n+3){border-bottom:1px solid rgba(255,255,255,.14)}
  .brand-strip .brand-cell:nth-child(3),
  .brand-strip .brand-cell:nth-child(5){border-right:0}
  .brand-strip::before,
  .brand-strip::after{content:"";background:var(--navy-deep);grid-row:2}
  .brand-strip::before{grid-column:1}
  .brand-strip::after{grid-column:6}
}

/* Article table of contents (WD-2607-004 p26). Sits at the head of the body and
   jumps to the h2 anchors. scroll-margin keeps a target clear of the fixed
   header, which would otherwise hide the heading you just jumped to. */
.na-toc{margin:0 0 clamp(28px,3.4vw,40px);padding:clamp(20px,2.4vw,28px) clamp(20px,2.6vw,30px);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md)}
.na-toc .na-toc-t{font-family:var(--body);font-weight:600;font-size:.74rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted);margin:0 0 12px}
.na-toc ul{list-style:none;margin:0;padding:0;display:grid;gap:2px}
.na-toc li{position:relative;padding:6px 0 6px 20px}
.na-toc li::before{content:"";position:absolute;left:2px;top:14px;width:7px;height:7px;
  border-right:2px solid var(--lblue);border-bottom:2px solid var(--lblue);transform:rotate(-45deg)}
.na-toc a{font-family:var(--body);font-size:.98rem;line-height:1.55;color:var(--navy);
  text-decoration:none;border-bottom:1px solid transparent;transition:color .2s,border-color .2s}
.na-toc a:hover{color:var(--lblue-deep);border-bottom-color:var(--lblue)}
.na-body h2[id]{scroll-margin-top:110px}
