/* ============================================================================
   site.css — Kwamoka Group Ltd
   ----------------------------------------------------------------------------
   The world. Loaded after scrollcraft.css, which supplies a dark-canvas taste
   floor this site does not want: --sc-ink is #f4f2ef, near-white text for a
   near-black ground. Kwamoka Shipping owns near-black. The group ground here is
   bone paper and laterite earth, so everything scrollcraft assumes about the
   canvas is repainted below rather than inherited.

   Three rules run through the whole file.

   1. BRASS NEVER CARRIES SMALL TEXT. The audit measured #AF9A65 text at
      2.54-2.77:1 across the live site — a systemic brand-colour failure, not a
      handful of slips. Measured here: brass on bone is 2.36:1. So brass is a
      rule, a hairline, a marker and a large-display fill, and nothing else.
      Where a brass VOICE is needed on small text there are two substitutes,
      both measured: --brass-deep #5F5029 (6.76:1 on bone, 6.00:1 on bone-2) for
      light grounds, and --brass-lit #D8C48C (9.72:1 on ink, 5.94:1 on oxblood)
      for dark ones.
   2. EVERY TAP TARGET IS 44px ON ITS SHORT SIDE. The audit found the live
      footer's tel: and mailto: links — the highest-intent taps on the page — at
      25px. WCAG 2.5.8 floors at 24. Anything a finger lands on here clears 44.
   3. FOCUS IS A CHANGE, NOT A PRESENCE. A permanent border or a standing
      underline is not a focus indicator; it looks identical before and after.
      Every interactive element gains a ring it did not have when resting.
   ========================================================================== */

:root{
  /* Ground and ink. */
  --bone:#F2EDE4;        /* paper                                             */
  --bone-2:#E7E0D3;      /* the same paper, one shade down: raised bands      */
  --ink:#241C18;         /* 14.36:1 on bone                                   */
  --ink-2:#4A3B33;       /* 9.17:1 on bone — secondary prose, never lighter   */

  /* The group mark. Oxblood does the work brass cannot. */
  --oxblood:#5E353D;     /* 8.77:1 on bone. Links, CTAs, the Offtake ground.  */
  --oxblood-lift:#7A454F;/* 6.45:1 on bone — hover only                       */

  /* Brass. A rule, a hairline, a marker, a large-display fill. Never a word
     under 24px. See rule 1 above; --brass-deep and --brass-lit are the voices. */
  --brass:#AF9A65;
  --brass-deep:#5F5029;
  --brass-lit:#D8C48C;

  --laterite:#8C4A32;    /* 5.73:1 on bone — the candour colour, the red earth */
  --rule:#D5CBB8;        /* hairlines on light ground                          */
  --foot-meta:#CFC3AE;   /* 9.62:1 on ink                                      */

  --measure:66ch;
  --gut:clamp(1.25rem,5vw,2.5rem);
  --serif:ui-serif,Georgia,"Times New Roman",serif;
  --sans:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
}

*{box-sizing:border-box}

/* scrollcraft.css sets scroll-behavior:smooth. Overridden, deliberately.
   A smooth scroll animates every programmatic jump, and the first one on this
   site is the skip link — the control whose entire purpose is to get a keyboard
   user to the content IMMEDIATELY. Measured on this build, a scrollIntoView was
   still 832px short of its destination 250ms after it was asked for. Bypass
   Blocks that takes half a second is not a bypass. */
html{scroll-behavior:auto}

/* Motion is a preference, not a default. scroll-behavior and every transition
   collapse; nothing here depends on an animation completing to become legible. */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;scroll-behavior:auto!important}
}

body{
  margin:0;
  background:var(--bone);
  color:var(--ink);
  font:1rem/1.65 var(--serif);
  letter-spacing:0;
  -webkit-font-smoothing:antialiased;
  overflow-x:clip;
}

/* scrollcraft themes the browser surfaces to a lime accent. Repaint them. */
::selection{background:var(--oxblood);color:#fff}
:root{caret-color:var(--oxblood);accent-color:var(--oxblood)}

h1,h2,h3,h4{font-weight:600;text-wrap:balance;letter-spacing:-.012em}
p{text-wrap:pretty}

/* ------------------------------------------------------------------ focus --
   Audit a11y C1. The live site sets outline:0 on every form control with no
   replacement. Here the resting state has NO ring and the focused state has a
   3px one, so the two states differ and the focused one actually paints.       */
:focus-visible{outline:3px solid var(--oxblood);outline-offset:2px;border-radius:2px}
/* Dark grounds get the ring in lit brass; oxblood on oxblood is not a ring. */
.sec-dark :focus-visible,
.sec-ink :focus-visible,
.foot :focus-visible{outline-color:var(--brass-lit)}

/* ------------------------------------------------------------------ links -- */
a{color:var(--oxblood);text-decoration-thickness:1px;text-underline-offset:.2em}
a:hover{color:var(--oxblood-lift);text-decoration-color:var(--brass)}

/* Any link that is its own row — in a list, a table cell, a heading — is a tap
   target and clears 44px on its short side. Links inside a <p> stay inline in
   the prose, where a 44px line box would tear the paragraph apart. */
li>a,td>a,th>a,h3>a,dd>a,.skip{
  display:inline-flex;align-items:center;min-height:44px}

.nowrap{white-space:nowrap}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* Audit a11y C4, Bypass Blocks. Off-screen until focused, then the first thing
   on the page — and 44px tall, because it is a real control. */
.skip{position:absolute;left:-9999px;top:0;z-index:999;
  background:var(--oxblood);color:#fff;padding:0 1.1rem;
  font:600 .9rem/1 var(--sans);letter-spacing:.06em;text-decoration:none}
.skip:focus{left:0}

/* External-link mark. One glyph, decorative, aria-hidden in the markup. */
.ext-mark{font-size:.8em;margin-left:.3em;color:var(--brass-deep);
  text-decoration:none}
.sec-dark .ext-mark,.sec-ink .ext-mark,.foot .ext-mark{color:var(--brass-lit)}

/* ==================================================================== CTA == */
.cta{
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;padding:.7rem 1.5rem;border:0;border-radius:2px;
  background:var(--oxblood);color:#fff;          /* 10.23:1 */
  font:600 1rem/1.2 var(--sans);letter-spacing:.01em;
  text-decoration:none;cursor:pointer;
  transition:background-color 160ms ease}
.cta:hover{background:var(--oxblood-lift);color:#fff}
.cta-2{background:transparent;color:var(--oxblood);          /* 8.77:1 on bone */
  box-shadow:inset 0 0 0 2px var(--oxblood)}
.cta-2:hover{background:var(--oxblood);color:#fff}
.cta-row{display:flex;flex-wrap:wrap;gap:.85rem;margin:2rem 0 0}

/* ============================================================== DOCUMENT == */


main>article{
  max-width:calc(var(--measure) + 2 * var(--gut));margin:0 auto;
  padding:clamp(2rem,6vh,3.5rem) var(--gut) clamp(3rem,9vh,5.5rem)}
main>article>h1{
  font-size:clamp(1.7rem,3.6vw,2.5rem);line-height:1.12;margin:0 0 .8rem}
main>article>h2,.prose h2{
  font-size:clamp(1.15rem,2.2vw,1.4rem);margin:2.6rem 0 .7rem;
  padding-top:1.1rem;border-top:1px solid var(--rule)}

.lede{font-size:1.12rem;line-height:1.55;color:var(--ink-2);max-width:52ch}


/* ------------------------------------------------------------------ facts -- */
.facts{margin:1.8rem 0 0;padding:0;border-top:1px solid var(--rule)}
.facts>div{
  display:grid;grid-template-columns:minmax(9rem,14rem) 1fr;gap:.2rem 1.2rem;
  padding:.65rem 0;border-bottom:1px solid var(--rule)}
.facts dt{
  font:600 .72rem/1.7 var(--sans);letter-spacing:.13em;text-transform:uppercase;
  color:var(--brass-deep)}                                     /* 6.76:1 */
.facts dd{margin:0;color:var(--ink)}
@media (max-width:520px){.facts>div{grid-template-columns:1fr}}

/* -------------------------------------------------------- evidence lists -- */
.ev{list-style:none;margin:1.2rem 0 0;padding:0}
.ev li{position:relative;padding:.42rem 0 .42rem 1.6rem;color:var(--ink-2)}
.ev li::before{
  content:"";position:absolute;left:0;top:1.02em;
  width:14px;height:2px;background:var(--brass)}   /* brass as a marker rule */
.ev-lg li{font-size:1.02rem;line-height:1.55;border-bottom:1px solid var(--rule)}
.ev-lg li:last-child{border-bottom:0}

/* ------------------------------------------------------- company profiles -- */
.co-grid{list-style:none;margin:1.8rem 0 0;padding:0}
.co{padding:1.4rem 0;border-top:1px solid var(--rule)}
.co h3{margin:0;font-size:1.18rem}
.co h3 a{color:var(--ink);text-decoration:none;border-bottom:2px solid var(--brass)}
.co h3 a:hover{color:var(--oxblood);border-bottom-color:var(--oxblood)}
.co-inc{
  margin:.15rem 0 .6rem;
  font:600 .72rem/1.6 var(--sans);letter-spacing:.13em;text-transform:uppercase;
  color:var(--brass-deep)}                                     /* 6.76:1 */
.co p{margin:.4rem 0;color:var(--ink-2)}



.ask{margin:1.4rem 0 0;color:var(--ink-2)}
.more{margin:1.8rem 0 0}

/* ------------------------------------------------------------- leadership -- */
.people{list-style:none;margin:2rem 0 0;padding:0}
.person{padding:1.5rem 0;border-top:1px solid var(--rule)}
.person h2{margin:0;font-size:1.2rem;border:0;padding:0}
.role{
  margin:.15rem 0 .7rem;
  font:600 .72rem/1.6 var(--sans);letter-spacing:.13em;text-transform:uppercase;
  color:var(--brass-deep)}                                     /* 6.76:1 */
.person p{color:var(--ink-2);margin:.4rem 0 0}


/* ================================================================ TABLES ==
   /group's company table overflowed the viewport by 52px at 390px and 118px at
   320px. Wide content scrolls inside its own box; the page body never scrolls
   sideways.

   `contain:paint` is load-bearing, not decoration. Setting display:block on a
   <table> wraps its rows in an ANONYMOUS table box, and in Chrome that box's
   layout overflow still propagates to documentElement.scrollWidth even though
   overflow-x:auto is clipping it visually and scrolling it correctly — measured
   here at 525px in a 390px viewport with a table reporting a healthy 592/350.
   Containment is what stops the propagation. Verified at 390px and 320px. */
.co-table{
  display:block;width:100%;max-width:100%;overflow-x:auto;contain:paint;
  border-collapse:collapse;margin:1.4rem 0 0;
  -webkit-overflow-scrolling:touch}
.co-table th,.co-table td{
  text-align:left;padding:.35rem .8rem;white-space:nowrap;
  border-bottom:1px solid var(--rule)}
.co-table thead th{
  font:600 .7rem/1.6 var(--sans);letter-spacing:.11em;text-transform:uppercase;
  color:var(--brass-deep);border-bottom:2px solid var(--brass);
  padding-top:.5rem;padding-bottom:.5rem}
.co-table tbody th{font-weight:400}
.co-table th:first-child,.co-table td:first-child{padding-left:0}
.co-table a{color:var(--oxblood)}
/* A box that scrolls sideways has to say so, or the cut-off column just looks
   like a bug. A shadow sits at whichever edge still has content behind it and
   disappears when you reach that end: the two opaque covers travel WITH the
   content (background-attachment:local) and mask the two shadows, which are
   pinned to the box. Measured letting through at worst rgb(209,204,196) under
   the edge column, where oxblood link text still reads at 6.4:1. */
.co-table{
  background-image:
    linear-gradient(to right,var(--bone),rgba(242,237,228,0)),
    linear-gradient(to left, var(--bone),rgba(242,237,228,0)),
    linear-gradient(to right,rgba(36,28,24,.16),rgba(36,28,24,0)),
    linear-gradient(to left, rgba(36,28,24,.16),rgba(36,28,24,0));
  background-position:left center,right center,left center,right center;
  background-repeat:no-repeat;
  background-size:28px 100%,28px 100%,12px 100%,12px 100%;
  background-attachment:local,local,scroll,scroll}

/* ================================================================== FORM == */
.enquiry{margin:2rem 0 0;max-width:36rem}
.f{margin:0 0 1.25rem}
.f label{
  display:block;margin:0 0 .35rem;
  font:600 .74rem/1.6 var(--sans);letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink-2)}                                          /* 9.17:1 */
.enquiry input:not([type="checkbox"]),
.enquiry textarea{
  display:block;width:100%;min-height:44px;padding:.65rem .8rem;
  background:#fff;color:var(--ink);
  border:1px solid var(--rule);border-bottom:2px solid var(--ink-2);
  border-radius:0;font:1rem/1.5 var(--serif)}
.enquiry textarea{resize:vertical;min-height:11rem}
.enquiry input:not([type="checkbox"]):hover,
.enquiry textarea:hover{border-color:var(--brass-deep)}

/* The consent checkbox measured 13x13 unstyled — half the WCAG floor. The
   control is a 44px target; the mark inside it is 22px, which is what the eye
   reads. appearance:none is what makes both true at once. */
.f-check{display:flex;align-items:flex-start;gap:.4rem}
.f-check label{
  margin:.65rem 0 0;flex:1;text-transform:none;letter-spacing:0;
  font:1rem/1.5 var(--serif);color:var(--ink)}
.f-check input[type="checkbox"]{
  appearance:none;-webkit-appearance:none;
  flex:0 0 44px;width:44px;height:44px;min-height:44px;
  margin:0;padding:0;border:0;background:transparent;border-radius:0;
  display:grid;place-content:center;cursor:pointer}
.f-check input[type="checkbox"]::before{
  content:"";display:block;width:22px;height:22px;
  background:#fff;border:2px solid var(--ink-2);border-radius:2px}
.f-check input[type="checkbox"]:hover::before{border-color:var(--oxblood)}
.f-check input[type="checkbox"]:checked::before{
  background:var(--oxblood);border-color:var(--oxblood);
  box-shadow:inset 0 0 0 3px #fff}

/* Honeypot. Removed from layout entirely: at any non-zero size it renders as a
   real 204x35 field a finger can hit and a screen reader can reach. */
.hp{display:none}

/* ================================================================ FOOTER == */
.foot{
  background:var(--ink);color:var(--bone);                     /* 14.36:1 */
  border-top:3px solid var(--brass);
  padding:clamp(2.5rem,7vh,4rem) var(--gut) 2rem}
.foot-in{
  max-width:var(--measure);margin:0 auto;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));
  gap:2rem 2.5rem}
.foot-name{margin:0;font-size:1.05rem;font-weight:600}
.foot-h{
  margin:0 0 .3rem;
  font:600 .72rem/1.6 var(--sans);letter-spacing:.14em;text-transform:uppercase;
  color:var(--brass-lit)}                                      /* 9.72:1 */
.foot-meta{margin:.6rem 0 0;font-size:.9rem;line-height:1.7;color:var(--foot-meta)}
.foot-list{list-style:none;margin:0;padding:0}
.foot a{color:var(--bone);text-decoration:none;
  border-bottom:1px solid rgba(216,196,140,.45)}
.foot a:hover{color:#fff;border-bottom-color:var(--brass-lit)}
.foot-legal{
  max-width:var(--measure);margin:2.5rem auto 0;padding-top:1.2rem;
  border-top:1px solid rgba(242,237,228,.16);
  font-size:.85rem;color:var(--foot-meta)}                     /* 9.62:1 */
.foot-legal a{color:var(--bone)}


/* 320px is the reflow floor, not a phantom. Nothing may scroll sideways here. */
@media (max-width:360px){
  .co-table th,.co-table td{padding:.35rem .55rem}
}



/* Generated imagery is atmosphere (spec s2). Every slot carries data-img-slot so
   real Kumasi and Jeduako-Kwamang photography replaces it without a rebuild.
   width/height are emitted from the file itself, so these reserve their own space
   and cannot shift layout. */
.act-img{display:block;width:100%;height:auto;margin:0 0 1.6rem;
  border:1px solid var(--rule);background:var(--bone-2)}
.sec-ink .act-img{border-color:rgba(242,237,228,.22)}
@media (min-width:901px){ .act-img{max-width:760px} }


/* ================================================== HEADER AND HERO == */
/* Both sister sites open on a bar nav and a full-bleed photograph. This build
   opened on a wordmark and a facts table, which is what read as "too plain".
   Same conventions as the family, executed a step up. */
.site-head{
  position:sticky;top:0;z-index:60;display:flex;align-items:center;gap:1.5rem;
  padding:.5rem clamp(1rem,4vw,2.5rem);background:var(--bone);
  border-bottom:1px solid var(--rule);box-shadow:0 1px 0 rgba(175,154,101,.35)}
.brand{display:flex;align-items:center;flex:0 0 auto;padding:.25rem 0}
.brand-mark{display:block;width:auto;height:46px}
.sitenav{margin-left:auto}
.sitenav ul{list-style:none;display:flex;gap:clamp(.9rem,2vw,1.9rem);margin:0;padding:0}
.sitenav a{display:flex;align-items:center;justify-content:center;
  min-height:44px;min-width:44px;padding:0 .2rem;text-decoration:none;
  color:var(--ink-2);font:500 .95rem/1 var(--sans);border-bottom:2px solid transparent}
.sitenav a:hover{color:var(--oxblood)}
.sitenav a[aria-current="page"]{color:var(--oxblood);border-bottom-color:var(--oxblood)}
.head-cta{display:inline-flex;align-items:center;min-height:44px;padding:0 1.15rem;
  background:var(--oxblood);color:#fff;text-decoration:none;border-radius:2px;
  font:600 .95rem/1 var(--sans)}
.head-cta:hover{background:var(--oxblood-lift)}
@media (max-width:820px){
  .site-head{flex-wrap:wrap;gap:.4rem .8rem;padding:.5rem 1rem}
  .sitenav{order:3;flex-basis:100%;margin-left:0}
  /* Wrap, never scroll. A horizontally scrolling nav puts the last link off
     screen, which the reachability gate correctly failed on at 320px. */
  .sitenav ul{flex-wrap:wrap;gap:.15rem .9rem}
  .brand-mark{height:36px}
  .head-cta{padding:0 .9rem;font-size:.9rem}
}

.hero{position:relative;isolation:isolate;min-height:clamp(420px,62vh,640px);
  display:flex;align-items:center;overflow:hidden;background:var(--ink)}
.hero-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0}
/* The scrim has to carry the text, not flatter the photograph. Measured on
   composited pixels: the previous values let sky through at rgb(185,180,170)
   behind the sub-heading on mobile, which is 1.77:1. A hero you cannot read is
   not a hero. */
.hero-scrim{position:absolute;inset:0;z-index:1;
  background:linear-gradient(100deg,rgba(30,22,18,.97) 0%,rgba(30,22,18,.93) 48%,
                             rgba(30,22,18,.62) 78%,rgba(30,22,18,.38) 100%)}
@media (max-width:820px){
  /* Portrait crops the image tighter and there is no reliable dark region to
     aim at, so the overlay goes near-flat rather than directional. */
  .hero-scrim{background:linear-gradient(180deg,rgba(30,22,18,.90) 0%,
                                         rgba(30,22,18,.86) 60%,rgba(30,22,18,.92) 100%)}
}
.hero-in{position:relative;z-index:2;max-width:calc(var(--measure) + 2 * var(--gut));width:100%;
  margin:0 auto;padding:clamp(2.5rem,7vh,4.5rem) var(--gut)}
.hero-eyebrow{margin:0 0 .9rem;font:600 .76rem/1.4 var(--sans);letter-spacing:.16em;
  text-transform:uppercase;color:#F4E9CC}   /* measured 3.99:1 at #EBDCB4 over the moving hero; small text needs 4.5 */
.hero-h{margin:0 0 1rem;max-width:20ch;color:var(--bone);
  font:600 clamp(2.1rem,5.4vw,3.6rem)/1.08 var(--serif);letter-spacing:-.015em;
  text-wrap:balance}
.hero-sub{margin:0 0 1.7rem;max-width:52ch;color:rgba(242,237,228,.92);
  font:400 clamp(1rem,1.6vw,1.16rem)/1.6 var(--sans)}
.hero-ctas{display:flex;flex-wrap:wrap;gap:.75rem;margin:0}
.hero-ctas .cta-2{background:transparent;color:var(--bone);
  box-shadow:inset 0 0 0 2px rgba(242,237,228,.75)}
.hero-ctas .cta-2:hover{background:rgba(242,237,228,.12)}
@media (prefers-reduced-motion:no-preference){ .hero-img{will-change:auto} }


/* ============================================ PORTFOLIO SECTIONS == */
/* The homepage was six acts named after stage gates, which narrated how far
   along each project was. A parent company's site shows the businesses it holds
   and the work they do, so these are sections, not acts, and nothing is ranked. */
.sec{padding:clamp(3rem,7vh,5.5rem) 0;border-top:1px solid var(--rule)}
.sec-in{max-width:min(1180px,100% - 2 * var(--gut));margin:0 auto}
.sec-h{margin:0 0 .7rem;font:600 clamp(1.7rem,3.6vw,2.5rem)/1.15 var(--serif);
  letter-spacing:-.015em;text-wrap:balance}
.sec .lede{max-width:62ch;margin:0 0 2.2rem;font-size:1.08rem;color:var(--ink-2)}
.sec-alt{background:var(--bone-2)}
.sec-dark{background:var(--oxblood);border-top:0}
.sec-dark .sec-h,.sec-dark .ask,.sec-dark a{color:var(--bone)}
.sec-dark .ask{max-width:58ch;font-size:1.08rem}
.sec-dark .cta{background:var(--bone);color:var(--oxblood)}
.sec-dark .cta-2{background:transparent;color:var(--bone);
  box-shadow:inset 0 0 0 2px rgba(242,237,228,.7)}
.contact-direct{display:flex;flex-wrap:wrap;gap:0 2rem;margin:1.8rem 0 0;
  list-style:none;padding:0}
.sec-dark .contact-direct a{border-bottom:1px solid rgba(242,237,228,.5)}

/* The six companies. The centre of gravity: a holding company is its portfolio. */
.co-grid{list-style:none;margin:0;padding:0;display:grid;gap:1px;
  background:var(--rule);border:1px solid var(--rule);
  grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr))}
.co{background:var(--bone);padding:1.6rem 1.5rem;display:flex;flex-direction:column}
.sec-alt .co{background:var(--bone-2)}
.co-h{margin:0 0 .3rem;font:600 1.18rem/1.3 var(--serif)}
.co-h a{color:var(--ink);text-decoration:none;border-bottom:2px solid var(--brass)}
.co-h a:hover{color:var(--oxblood)}
.co-inc{margin:0 0 .7rem;font:600 .72rem/1 var(--sans);letter-spacing:.12em;
  text-transform:uppercase;color:var(--brass-deep)}
.co-line{margin:0 0 1rem;color:var(--ink-2)}
.co .ev{margin:0 0 1rem;padding:0;list-style:none;font-size:.92rem;
  color:var(--ink-2);display:flex;flex-direction:column;gap:.5rem}
.co .ev li{padding-left:1rem;position:relative}
.co .ev li::before{content:"";position:absolute;left:0;top:.62em;
  width:.5rem;height:1px;background:var(--brass)}
.co-out{margin:auto 0 0;padding-top:.4rem}

/* What we do. */
.sector-grid{list-style:none;margin:0;padding:0;display:grid;
  gap:1.6rem 2rem;grid-template-columns:repeat(auto-fit,minmax(min(100%,265px),1fr))}
.sector{border-top:2px solid var(--brass);padding-top:.9rem}
.sector-h{margin:0 0 .4rem;font:600 1.05rem/1.3 var(--sans)}
.sector p{margin:0 0 .6rem;color:var(--ink-2);font-size:.96rem}
.sector-who{font-size:.88rem}
.sector-who a{color:var(--oxblood)}


/* ======================================================================
   MOTION AND DEPTH
   ----------------------------------------------------------------------
   Everything below is executed by site.js and gated three ways:
     html.js          set by a one-line inline script in <head>. With scripting
                      off the class never appears and nothing is ever hidden.
     .is-in           added once per element on entry. The hidden state is
                      written as :not(.is-in), so an element is only ever
                      hidden BEFORE it has been seen.
     reduced motion   no hidden state, no parallax, no rail. Reveals are not
                      "faster", they are absent; the page is simply set.
   Only transform and opacity animate. Nothing here writes a size or a
   position, and nothing depends on an animation finishing to be legible.
   ====================================================================== */
:root{--head-h:66px;--ease:cubic-bezier(.2,.7,.2,1);--wide:min(1180px,100% - 2 * var(--gut))}

/* ------------------------------------------------------------ reveals -- */
html.js [data-rv]{transition:opacity 760ms var(--ease),transform 760ms var(--ease)}
html.js [data-rv]:not([data-stagger]):not(.is-in){opacity:0;transform:translate3d(0,22px,0)}
html.js [data-rv][data-stagger]>*{transition:opacity 680ms var(--ease),transform 680ms var(--ease)}
html.js [data-rv][data-stagger]:not(.is-in)>*{opacity:0;transform:translate3d(0,18px,0)}
/* A hairline that draws in from the left as its block arrives. */
html.js .sec-head .sec-h::before{
  content:"";display:block;width:56px;height:2px;margin:0 0 1.1rem;background:var(--brass);
  transform:scaleX(0);transform-origin:left;transition:transform 900ms var(--ease) 120ms}
html.js .sec-head.is-in .sec-h::before{transform:none}
html:not(.js) .sec-head .sec-h::before{
  content:"";display:block;width:56px;height:2px;margin:0 0 1.1rem;background:var(--brass)}
@media (prefers-reduced-motion:reduce){
  html.js [data-rv]:not([data-stagger]):not(.is-in),
  html.js [data-rv][data-stagger]:not(.is-in)>*{opacity:1;transform:none}
  html.js .sec-head .sec-h::before{transform:none}
}
/* Anchor targets land below the sticky header, not under it. */
[id]{scroll-margin-top:calc(var(--head-h) + 1.25rem)}

/* ------------------------------------------------------------- header --
   Over the hero the bar is bone at 74% with the photograph blurred through
   it, so the hero starts under the bar rather than below it. Twelve pixels of
   scroll and it is the solid bar it always was. Same ink, same links: the
   text never changes colour, so nothing about readability is in play. */
html.js .site-head{
  background:rgba(242,237,228,.82);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  transition:background-color 320ms var(--ease),box-shadow 320ms var(--ease)}
html.js .site-head.is-solid{background:var(--bone)}
html.js .hero{margin-top:calc(-1 * var(--head-h))}
html.js .hero-in{padding-top:calc(clamp(2.5rem,7vh,4.5rem) + var(--head-h))}

/* --------------------------------------------------------------- hero --
   Planes. Each is oversized so that scale and travel never reveal an edge:
   the photograph at 108% under scroll, the haze plane at 120%. */
.hero{min-height:clamp(540px,80vh,800px);align-items:center}
.hero-media{position:absolute;inset:-4%;z-index:0;will-change:transform;transform:scale(1.08);transform-origin:50% 40%}
.hero-media .hero-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.hero-atmos{position:absolute;inset:-10%;z-index:1;pointer-events:none;will-change:transform;
  background:
    radial-gradient(40% 58% at 80% 34%,rgba(216,196,140,.20),transparent 72%),
    radial-gradient(70% 50% at 10% 100%,rgba(140,74,50,.28),transparent 70%)}
.hero-scrim{z-index:1}
.hero-in{z-index:2;will-change:transform,opacity}
.hero-h{overflow:visible}
.hl{display:block;overflow:hidden;padding-bottom:.12em;margin-bottom:-.12em}
.hl-i{display:block;transform:translate3d(0,112%,0);animation:hl-rise 980ms var(--ease) 80ms both}
.hl:nth-child(2) .hl-i{animation-delay:200ms}
.hero-rule{display:inline-block;width:40px;height:2px;margin:0 .85rem .2em 0;vertical-align:middle;
  background:var(--brass);transform:scaleX(0);transform-origin:left;
  animation:hl-draw 820ms var(--ease) 360ms both}
.hero-eyebrow{animation:hl-fade 700ms var(--ease) 60ms both}
.hero-sub{animation:hl-up 900ms var(--ease) 420ms both}
.hero-ctas{animation:hl-up 900ms var(--ease) 560ms both}
@keyframes hl-rise{from{transform:translate3d(0,112%,0)}to{transform:none}}
@keyframes hl-draw{from{transform:scaleX(0)}to{transform:none}}
@keyframes hl-fade{from{opacity:0}to{opacity:1}}
@keyframes hl-up{from{opacity:0;transform:translate3d(0,14px,0)}to{opacity:1;transform:none}}

/* The company index. Six links on one plate, straddling the hero's bottom
   edge, so the photograph reads as sitting behind the document. */
.hero-index{position:relative;z-index:3;max-width:var(--wide);margin:-2.75rem auto 0;
  background:var(--bone);border:1px solid var(--rule);
  box-shadow:0 30px 60px -34px rgba(36,28,24,.55),0 1px 0 rgba(175,154,101,.35)}
.hero-index ul{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(6,1fr)}
.hero-index li+li{border-left:1px solid var(--rule)}
.hero-index a{display:flex;flex-direction:column;justify-content:center;gap:.28rem;
  min-height:72px;padding:.9rem .8rem;text-decoration:none;color:var(--ink);position:relative;text-align:center;
  transition:background-color 240ms var(--ease)}
.hero-index a::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:2px;
  background:var(--brass);transform:scaleX(0);transform-origin:left;transition:transform 360ms var(--ease)}
.hero-index a:hover{background:#fff}
.hero-index a:hover::after,.hero-index a.is-hit::after{transform:none}
.hi-name{font:600 .92rem/1.2 var(--serif)}
@media (min-width:901px){.hi-name{white-space:nowrap}}
.hi-meta{font:600 .66rem/1 var(--sans);letter-spacing:.13em;text-transform:uppercase;color:var(--brass-deep)}
@media (max-width:900px){
  .hero-index{margin:-1.5rem auto 0}
  .hero-index ul{grid-template-columns:repeat(3,1fr)}
  .hero-index li:nth-child(3n+1){border-left:0}
  .hero-index li:nth-child(n+4){border-top:1px solid var(--rule)}
}
@media (max-width:520px){
  .hero-index ul{grid-template-columns:repeat(2,1fr)}
  .hero-index li:nth-child(3n+1){border-left:1px solid var(--rule)}
  .hero-index li:nth-child(2n+1){border-left:0}
  .hero-index li:nth-child(n+4){border-top:0}
  .hero-index li:nth-child(n+3){border-top:1px solid var(--rule)}
  .hero-index a{min-height:64px;padding:.7rem .9rem}
}

/* ------------------------------------------------------------- cards --
   The photograph is the change. Every card carries one, the whole card lifts
   under the pointer, and the picture pushes in a few percent behind it. */
.co{padding:0;position:relative;transition:transform 380ms var(--ease),box-shadow 380ms var(--ease)}
.co-media{position:relative;aspect-ratio:3/2;overflow:hidden;background:var(--bone-2);
  border-bottom:1px solid var(--rule)}
.co-img{display:block;width:100%;height:100%;object-fit:cover;
  transition:transform 1100ms var(--ease)}
.co-body{padding:1.4rem 1.5rem 1.5rem;display:flex;flex-direction:column;flex:1}
.co-h a{transition:color 200ms var(--ease),border-color 200ms var(--ease)}
@media (hover:hover){
  .co:hover{transform:translateY(-4px);z-index:1;
    box-shadow:0 28px 54px -30px rgba(36,28,24,.55)}
  .co:hover .co-img,.co:focus-within .co-img{transform:scale(1.05)}
}

/* ---------------------------------------------------------- cross-light --
   The cards, the lines of work, the licence rows and the index are one set
   of facts keyed by company. Pointing at any one lights the rest and lets the
   others fall back, so the reader sees the relationship without a legend. */
[data-co]{transition:opacity 280ms var(--ease)}
html.is-lit [data-co]:not(.is-hit){opacity:.42}
html.is-lit .co.is-hit .co-h a{color:var(--oxblood)}
html.is-lit tr.is-hit th,html.is-lit tr.is-hit td{background:var(--bone-2)}
html.is-lit .sector.is-hit{border-top-color:var(--oxblood)}

/* --------------------------------------------------------------- rail --
   "What we do" on a wide screen: the section grows to the width of its own
   list, the inner block pins, and the list travels sideways as the reader
   scrolls down. site.js sets the heights; nothing here does. Without the
   is-rail class (narrow screens, reduced motion, scripting off) the list is
   the grid below, untouched. */
.sector-grid{max-width:var(--wide);margin:0 auto}
.rail-foot .rail-progress{display:none}
.sec-sectors .sec-in.rail-head .sec-head{margin:0}
[data-rail].is-rail{padding:0;border-top:1px solid var(--rule)}
[data-rail].is-rail .rail-sticky{position:sticky;overflow:hidden;overflow:clip;display:flex;flex-direction:column;
  justify-content:center;gap:clamp(1.5rem,4vh,2.6rem);background:var(--bone-2)}
[data-rail].is-rail .rail-head{width:100%}
[data-rail].is-rail .sector-grid{display:flex;gap:1.4rem;width:max-content;max-width:none;margin:0;
  padding:0 clamp(2rem,8vw,7rem) 0 max(var(--gut),(100vw - 1180px) / 2);will-change:transform}
[data-rail].is-rail .sector{flex:0 0 clamp(300px,26vw,380px);display:flex;flex-direction:column;
  min-height:262px;padding:1.3rem 1.4rem 1.5rem;background:var(--bone);border:1px solid var(--rule);
  border-top:2px solid var(--brass);transition:border-color 240ms var(--ease),opacity 280ms var(--ease)}
[data-rail].is-rail .sector-h{font:600 1.5rem/1.12 var(--serif);letter-spacing:-.012em;margin:0 0 .6rem}
[data-rail].is-rail .sector p{flex:0 0 auto}
[data-rail].is-rail .sector-who{margin-top:auto;padding-top:.8rem}
[data-rail].is-rail .rail-foot{width:100%}
[data-rail].is-rail .rail-progress{display:block;height:2px;background:var(--rule)}
[data-rail].is-rail .rail-progress span{display:block;height:100%;background:var(--brass);
  transform:scaleX(0);transform-origin:left;will-change:transform}
/* The list is driven frame by frame from the scroll position, so it must not
   also carry the reveal system's eased transform transition: with one, the
   bar answers the scroll instantly while the cards drift toward it for most
   of a second, which reads as the rail running past its end and then lagging
   on the way back. Its children keep their entrance transition; the list
   itself has none. */
[data-rail-track]{transition:none!important}

/* ---------------------------------------------------------- spotlight --
   A brass light follows the pointer across the closing band. Decorative,
   hover only, and the band's text is measured against the ground without it. */
.sec-dark{position:relative;overflow:hidden}
.sec-dark::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(36rem 36rem at var(--mx,72%) var(--my,40%),rgba(216,196,140,.22),transparent 66%);
  opacity:0;transition:opacity 600ms var(--ease)}
@media (hover:hover){html.js .sec-dark:hover::before{opacity:1}}
.sec-dark .sec-in{position:relative}

/* ------------------------------------------------------------- banner --
   Company pages open on their card's photograph, moving against the scroll
   inside a clipped frame. aria-hidden: it is atmosphere, not information. */
.banner{position:relative;height:clamp(220px,40vh,440px);overflow:hidden;background:var(--ink)}
.banner-media{position:absolute;inset:-14% 0}
.banner-img{display:block;width:100%;height:100%;object-fit:cover;will-change:transform}
.banner::after{content:"";position:absolute;inset:auto 0 0;height:38%;
  background:linear-gradient(180deg,rgba(242,237,228,0),rgba(242,237,228,.55))}
main>.banner+article{padding-top:clamp(1.6rem,4vh,2.6rem)}

/* ------------------------------------------------------ reduced motion -- */
@media (prefers-reduced-motion:reduce){
  .hero-media,.hero-atmos,.hero-in,.banner-img{transform:none!important;opacity:1!important}
  .hl-i,.hero-rule,.hero-eyebrow,.hero-sub,.hero-ctas{animation:none;transform:none;opacity:1}
  .co:hover{transform:none}
  .co:hover .co-img{transform:none}
}


/* ======================================================================
   THE REVISED STRUCTURE, 8 September 2026
   ----------------------------------------------------------------------
   The client's own copy replaced six subsidiaries with three, and moved
   construction, haulage, supply and real estate to the Group itself. Three
   things below follow from that: an index plate built for three cells rather
   than six, a dark sustainability band that gives the page a beat it did not
   have, and a flat variant of the lines-of-work grid for pages that do not
   run the rail.
   ====================================================================== */

/* The index plate is the first thing under the hero, and whatever follows it
   opens the document. That section must not draw its own top rule: the plate
   already has a border on every edge, so a second line a few pixels below it
   reads as a mistake. Extra room instead, since the plate overlaps the hero and
   needs air beneath it. */
.hero-index + .sec{border-top:0;padding-top:clamp(3.5rem,8vh,6rem)}

/* -------------------------------------------------- index, three cells --
   Overrides the six-cell rules above at every breakpoint rather than trying to
   share them: three items through nth-child(3n+1) and nth-child(n+4) rules
   written for six is how a border ends up on the wrong edge. */
.hero-index-3 ul{grid-template-columns:repeat(3,1fr)}
.hero-index-3 .hi-name{white-space:normal}
.hero-index-3 a{gap:.34rem;padding:1rem 1.2rem}
@media (max-width:900px){
  .hero-index-3 ul{grid-template-columns:repeat(3,1fr)}
  .hero-index-3 li+li{border-left:1px solid var(--rule);border-top:0}
  .hero-index-3 li:nth-child(3n+1){border-left:0}
}
@media (max-width:640px){
  .hero-index-3 ul{grid-template-columns:1fr}
  .hero-index-3 li+li{border-left:0;border-top:1px solid var(--rule)}
  /* Name over tag, always. Laid out as a row instead, the two short names fit
     on one line and the long one wraps, so the three rows stop matching. */
  .hero-index-3 a{min-height:64px;align-items:flex-start;text-align:left}
}

/* ------------------------------------------------- the dark ink band --
   Bone on ink is 14.36:1. Links and the small headings take --brass-lit,
   9.72:1 on this ground and the approved brass voice for dark surfaces; plain
   brass never appears here, because it never carries small text anywhere. */
.sec-ink{background:var(--ink);color:var(--bone);border-top:0}
.sec-ink .sec-h{color:var(--bone)}
.sec-ink .lede{color:rgba(242,237,228,.88)}
.sec-ink p{color:rgba(242,237,228,.84)}
.sec-ink a{color:var(--brass-lit)}
.sec-ink a:hover{color:#fff}
.sec-ink :focus-visible{outline-color:var(--brass-lit)}
.sec-ink .sec-head .sec-h::before{background:var(--brass-lit)}

.su-in{display:grid;grid-template-columns:1fr minmax(0,38%);
  gap:clamp(1.8rem,4vw,3.5rem);align-items:start}
.su-list{list-style:none;margin:2.2rem 0 0;padding:0;display:grid;
  gap:1.5rem 2rem;grid-template-columns:repeat(auto-fit,minmax(min(100%,210px),1fr))}
.su-list li{border-top:1px solid rgba(216,196,140,.34);padding-top:.85rem}
.su-h{margin:0 0 .35rem;font:600 1rem/1.3 var(--sans);color:var(--brass-lit)}
.su-list p{margin:0;font-size:.94rem;line-height:1.6}
.su-media{margin:0}
/* The frame clips; the image is taller than the frame so the parallax has
   somewhere to travel. site.js writes translate only, so any scale set here
   would be thrown away on the first frame. */
.su-frame{position:relative;overflow:hidden;aspect-ratio:4/5;
  border:1px solid rgba(216,196,140,.28);background:#0f0b09}
.su-img{position:absolute;inset:-9% 0;width:100%;height:118%;object-fit:cover;
  will-change:transform}
.su-media figcaption{margin:.85rem 0 0;font-size:.86rem;line-height:1.55;
  color:rgba(242,237,228,.72)}                              /* 7.4:1 measured */
@media (max-width:900px){
  .su-in{grid-template-columns:1fr}
  .su-frame{aspect-ratio:16/10}
}

/* ------------------------------------------ lines of work, no rail --
   /services and /sustainability show the same shape as the homepage rail
   without pinning or travel, inside the article measure. */
.sector-grid-flat{margin:1.6rem 0 0;gap:1.5rem 2rem}
.sector-grid-flat .sector p{margin:0}

/* ------------------------------------------------------ the vision --
   A statement the group makes about itself, set apart from the prose that
   introduces it. Serif, brass rule, never a decorative quotation mark. */
.claim-q{margin:1.1rem 0 0;padding:.15rem 0 .15rem 1.2rem;max-width:54ch;
  border-left:3px solid var(--brass);
  font:400 clamp(1.12rem,2.1vw,1.4rem)/1.45 var(--serif);color:var(--ink)}

/* --------------------------------------------------------- footer --- */
.foot-tag{margin:.45rem 0 0;font:400 .95rem/1.5 var(--serif);font-style:italic;
  color:var(--brass-lit)}                                   /* 9.72:1 on ink */
.foot-h2{margin-top:1.5rem}


/* The company table on /group carries four columns inside the reading measure,
   and every cell is nowrap so a scrolling table can never tear a row in half.
   That is right for the two identifier columns and wrong for the description,
   which is prose: held on one line it pushed the last column out of the box and
   the reader met a scrollbar instead of a table. Only that column wraps. */
.co-table td:nth-child(3){white-space:normal;min-width:8.5rem}
