/* ============================================================
   HAMZA MOHAMMED TRADING — motion and refinements
   Loaded after site.css.

   Kept in its own file on purpose: site.css stays a recognisable
   copy of the reference build in Ref/assets, so the two can still
   be diffed. Everything added after the static build lives here
   and can be removed in one line if the design changes direction.

   Everything below is suppressed for visitors who have asked for
   reduced motion — see the media query at the end.
   ============================================================ */


/* ============================================================
   1. SCROLL PROGRESS
   A hairline that fills as the page is read. Sits above the top
   bar and below the mobile menu.
   ============================================================ */
.sprog{
  position:fixed;
  top:0; left:var(--rail); right:0;
  height:2px;
  z-index:94;
  background:var(--orange);
  transform:scaleX(0);
  transform-origin:left center;
  will-change:transform;
  pointer-events:none;
}


/* ============================================================
   2. REVEALS
   site.js already adds .in to .rv when it scrolls into view.
   These rules hang extra motion off that same signal, so there
   is no second observer and no second source of truth.
   ============================================================ */

/* Images settle out of a slight zoom rather than just appearing. */
.rv .hx img,
.rv .phex img,
.rv.tile img{
  transform:scale(1.14);
  transition:transform 1.5s var(--e), opacity .6s var(--e);
}
.rv.in .hx img,
.rv.in .phex img,
.rv.in.tile img{ transform:scale(1); }
/* The tile keeps its own hover zoom, which must win. */
.rv.in.tile:hover img{ transform:scale(1.06); }

/* Headings wipe in from the leading edge. Kept a touch quicker than the
   block fade around it, so the heading never lags behind its own section. */
.rv h2,
.rv h3{
  clip-path:inset(0 100% 0 0);
  transition:clip-path .8s var(--e) .05s;
}
.rv.in h2,
.rv.in h3{ clip-path:inset(0 0 0 0); }

/* Bullet lists, tag chips and table rows arrive one after another.
   --i is set per child in enhance.js. */
[data-stagger] > *{
  opacity:0;
  transform:translateY(14px);
  transition:opacity .6s var(--e), transform .7s var(--e);
  transition-delay:calc(var(--i, 0) * 70ms);
}
[data-stagger].in > *{
  opacity:1;
  transform:none;
}


/* ============================================================
   3. HERO PARALLAX
   The photograph drifts slower than the page. Driven by a
   custom property so the compositor does the work.
   ============================================================ */
.hero-img img{
  transform:translate3d(0, var(--par, 0px), 0) scale(1.06);
  will-change:transform;
}


/* ============================================================
   4. HOME PROJECTS — the showcase
   Replaces the cursor-chasing thumbnail. Photo panel on one
   side, the numbered index on the other.
   ============================================================ */
.pshow{
  display:grid;
  grid-template-columns:minmax(0,.86fr) minmax(0,1.14fr);
  gap:clamp(28px,4vw,72px);
  margin-top:clamp(30px,4vw,54px);
  align-items:start;
}

/* --- the photo panel --- */
.pshow-media{ position:sticky; top:120px; }

.pshow-frame{
  position:relative;
  aspect-ratio:4/3.4;
  overflow:hidden;
  background:var(--navy-d);
  clip-path:polygon(9% 0, 100% 0, 91% 100%, 0 100%);
}
.pshow-img{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
  opacity:0;
  transform:scale(1.08);
  transition:opacity .75s var(--e), transform 1.4s var(--e);
}
.pshow-img.on{ opacity:1; transform:scale(1); }

/* A wash of navy so white type over any photograph stays legible. */
.pshow-frame::after{
  content:"";
  position:absolute; inset:0;
  background:linear-gradient(to top, rgba(11,22,38,.55), rgba(11,22,38,0) 55%);
  pointer-events:none;
}

.pshow-cap{
  position:relative;
  height:1.6em;
  margin-top:18px;
  padding-left:9%;
}
.pshow-cap-item{
  position:absolute; inset:0;
  font-family:var(--d); font-weight:700;
  font-size:10px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--orange);
  opacity:0;
  transform:translateY(6px);
  transition:opacity .45s var(--e), transform .45s var(--e);
}
.pshow-cap-item.on{ opacity:1; transform:none; }

/* --- the index --- */
.pshow-row{
  display:grid;
  grid-template-columns:52px minmax(0,1fr) auto;
  gap:clamp(12px,1.6vw,26px);
  align-items:center;
  padding:clamp(16px,2vw,26px) 0;
  border-top:1px solid rgba(255,255,255,.12);
  position:relative;
}
.pshow-row:last-of-type{ border-bottom:1px solid rgba(255,255,255,.12); }

/* The orange rule that draws itself under the active row. */
.pshow-row::after{
  content:"";
  position:absolute; left:0; bottom:-1px;
  width:100%; height:1px;
  background:var(--orange);
  transform:scaleX(0);
  transform-origin:left center;
  transition:transform .55s var(--e);
}
.pshow-row.active::after,
.pshow-row:hover::after,
.pshow-row:focus-visible::after{ transform:scaleX(1); }

.pshow-row .n{
  font-family:var(--d); font-weight:600;
  font-size:11px; letter-spacing:.16em;
  color:var(--orange);
  transition:opacity .4s;
  opacity:.55;
}
.pshow-row.active .n,
.pshow-row:hover .n{ opacity:1; }

.pshow-body h3{
  font-family:var(--d); font-weight:800;
  font-size:clamp(17px,2.1vw,30px);
  text-transform:uppercase; letter-spacing:-.035em;
  margin:0; line-height:1.05;
  transition:transform .55s var(--e), color .45s var(--e);
}
.pshow-row.active .pshow-body h3,
.pshow-row:hover .pshow-body h3{ transform:translateX(10px); color:var(--orange); }

.pshow-body .m{
  display:block;
  font-family:var(--d); font-weight:500;
  font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  color:#8FA0B8;
  margin-top:7px;
  transition:transform .55s var(--e);
}
.pshow-row.active .pshow-body .m,
.pshow-row:hover .pshow-body .m{ transform:translateX(10px); }

.pshow-row .ar{
  color:rgba(255,255,255,.25);
  font-size:18px;
  transition:color .4s, transform .4s var(--e);
}
.pshow-row.active .ar,
.pshow-row:hover .ar{ color:var(--orange); transform:translateX(6px); }

/* The per-row thumbnail only exists where hover does not. */
.pshow-thumb{ display:none; }

/* --- see all --- */
.pshow-all{
  display:inline-flex; align-items:center; gap:12px;
  margin-top:clamp(24px,3vw,38px);
  font-family:var(--d); font-weight:700;
  font-size:11.5px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--ivory);
  padding-bottom:6px;
  position:relative;
}
.pshow-all::after{
  content:"";
  position:absolute; left:0; bottom:0;
  width:100%; height:2px;
  background:var(--orange);
  transform:scaleX(0); transform-origin:right center;
  transition:transform .5s var(--e);
}
.pshow-all:hover{ color:var(--orange); }
.pshow-all:hover::after{ transform:scaleX(1); transform-origin:left center; }
.pshow-all i{ font-style:normal; transition:transform .4s var(--e); }
.pshow-all:hover i{ transform:translateX(6px); }


/* ============================================================
   5. SMALL REFINEMENTS
   ============================================================ */

/* The ribbon reacts to scrolling — but the speed is no longer applied by
   rewriting animation-duration.

   Changing the duration of a running CSS animation does not change its
   speed from where it is: the engine recomputes progress as elapsed time
   over the NEW duration, so the strip teleports to a different point in
   the cycle. Every scroll event did that, and the 320ms settle back to
   the resting speed did it again in the other direction — which is the
   "it resets and starts the roll again" you can see.

   enhance.js now integrates the position frame by frame instead and puts
   .rb-js on the track once it has taken over, so a speed change alters
   the rate and can never alter the position. The CSS animation below is
   the no-JS fallback and stays exactly as site.css declared it. */
.rb-in.rb-js{ animation:none !important; will-change:transform; }

/* Give the floating WhatsApp button one nudge on arrival so it is
   noticed, then leave it alone. */
@keyframes wa-in{
  0%{ transform:scale(0) rotate(-25deg); opacity:0 }
  70%{ transform:scale(1.12) rotate(4deg) }
  100%{ transform:scale(1) rotate(0); opacity:1 }
}
.wa{ animation:wa-in .85s var(--e) 1.1s both; }

/* Focus rings on the dark sections need to be visible. */
.px :focus-visible,
.pd.dark :focus-visible{ outline-color:var(--orange); outline-offset:4px; }


/* ============================================================
   6. RESPONSIVE
   ============================================================ */
@media (max-width:1000px){
  .pshow{ grid-template-columns:1fr; gap:clamp(24px,4vw,40px); }

  /* Below this width there is no hover, so the panel stops being
     useful: each row carries its own picture instead. */
  .pshow-media{ display:none; }

  .pshow-row{ grid-template-columns:44px 92px minmax(0,1fr) auto; }
  .pshow-thumb{
    display:block;
    width:92px; aspect-ratio:1/1;
    overflow:hidden;
    background:var(--navy-d);
    clip-path:polygon(12% 0, 100% 0, 88% 100%, 0 100%);
  }
  .pshow-thumb img{ width:100%; height:100%; object-fit:cover; }
  .pshow-body h3{ font-size:clamp(15px,3.4vw,20px); }
}

@media (max-width:760px){
  .pshow-row{ grid-template-columns:34px 64px minmax(0,1fr); row-gap:4px; }
  .pshow-thumb{ width:64px; }
  .pshow-row .ar{ display:none; }
  .sprog{ left:var(--rail); }
}


/* ============================================================
   7. REDUCED MOTION
   Everything above is decoration. If the visitor has asked their
   system for less movement, they get the layout and none of it.
   ============================================================ */
@media (prefers-reduced-motion: reduce){
  .sprog{ display:none; }

  .rv .hx img, .rv .phex img, .rv.tile img,
  .rv.in .hx img, .rv.in .phex img, .rv.in.tile img{ transform:none; }

  .rv h2, .rv h3,
  .rv.in h2, .rv.in h3{ clip-path:none; transition:none; }

  [data-stagger] > *{ opacity:1; transform:none; transition:none; }

  .hero-img img{ transform:none; }

  .pshow-img{ transition:opacity .01ms; }
  .pshow-img.on{ transform:none; }

  .wa{ animation:none; }
}

/* ============================================================
   8. BUTTON FILL — corner fix

   The skewed hover fill sits at translateX(-102%) at rest. But
   skewX(-31.5deg) pushes the top edge right again by roughly
   tan(31.5°) x half the button height — about 15px on a normal
   button. 102% of the width only buys back a few pixels, so the
   top corner of the fill stayed inside the button: the pale
   triangle in the top-left of every orange button.

   Widening the fill past both ends means the same -102% now
   carries it fully clear, and it also guarantees the fill covers
   the corners on hover rather than leaving slivers.
   ============================================================ */
.quote::before,
.bt-o::before,
.bt-g::before,
.bt-n::before{
  inset:-2px -1.6em;
}

/* ============================================================
   9. PRINT

   Quotations get printed and filed. A contact page that prints
   as a black rectangle with a floating WhatsApp badge is no use
   to anyone, so the print sheet strips the furniture and keeps
   the facts: addresses, phone numbers, product text, the EN 124
   table.
   ============================================================ */
@media print{
  /* Nothing that only exists for navigation or decoration. */
  .rail, .top, .sheet, .burg, .wa, .sprog, .ribbon,
  .hero-img, .phero-img, .pshow-media, .pshow-all,
  .form, .form-errors, .bt, .quote{ display:none !important; }

  body{
    padding-left:0 !important;
    background:#fff !important;
    color:#000 !important;
    font-size:11pt;
  }

  /* Reveals never run without a scroll, so force everything visible. */
  .rv, [data-stagger] > *{ opacity:1 !important; transform:none !important; }
  .rv h2, .rv h3{ clip-path:none !important; }

  .dark, .px, .pd, .hero, .phero, .ft{
    background:#fff !important;
    color:#000 !important;
    padding-block:12pt !important;
  }
  .o, .tag, .n, .pno{ color:#000 !important; }

  h1, h2, h3{ page-break-after:avoid; }
  .prow, .card, .tbl tr{ page-break-inside:avoid; }

  /* Print the destination of a link, since a printed page cannot be clicked. */
  .ct-list a::after{ content:" (" attr(href) ")"; font-size:9pt; }

  .tbl{ border-collapse:collapse; width:100%; }
  .tbl th, .tbl td{ border:1px solid #999 !important; padding:4pt 6pt !important; color:#000 !important; }

  /* The page needs to say who it is from once the header is gone. */
  .ft::before{
    content:"Hamza Mohammed Trading Establishment · +962 79 655 5862 · hmetrading.com";
    display:block;
    font-weight:700;
    border-top:2px solid #000;
    padding-top:8pt;
    margin-top:8pt;
  }
}

/* ============================================================
   10. DEPTH AND TEXTURE

   The reference palette is strong, but the dark areas are flat
   fields of a single navy. These four layers give them depth
   without adding any new colour to the brand:

     grain      a very faint film grain over everything
     grid       a drafting grid in the dark sections
     bloom      an off-centre orange glow behind them
     vignette   focus pulled towards the centre of the hero

   All of it is decorative: pointer events are off, none of it
   sits above interactive chrome, and print drops the lot.
   ============================================================ */

/* --- film grain ------------------------------------------------------
   An SVG turbulence tile held at very low opacity. It stops large flat
   areas from banding and gives the navy the feel of something printed
   rather than something filled. */
body::after{
  content:"";
  position:fixed; inset:0;
  z-index:70;
  pointer-events:none;
  opacity:.05;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.75' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='300' height='300' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* --- drafting grid ---------------------------------------------------
   64px squares, faded out towards the bottom so it reads as a drawing
   sheet the content sits on rather than as wallpaper. The subject is
   drainage and load classes; a drawing grid belongs here. */
.hero::before,
.px::before,
.pd::before{
  content:"";
  position:absolute; inset:0;
  z-index:0;
  pointer-events:none;
  background-image:
    linear-gradient(rgba(255,255,255,.032) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.032) 1px, transparent 1px);
  background-size:64px 64px;
  -webkit-mask-image:radial-gradient(ellipse 80% 60% at 50% 0%, #000 10%, transparent 75%);
          mask-image:radial-gradient(ellipse 80% 60% at 50% 0%, #000 10%, transparent 75%);
}

/* --- bloom -----------------------------------------------------------
   A single soft orange light, off to one side. Enough to lift the navy
   off itself; not enough to read as a colour in its own right. */
.px::after,
.pd::after{
  content:"";
  position:absolute;
  top:-20%; left:-10%;
  width:70%; aspect-ratio:1/1;
  z-index:0;
  pointer-events:none;
  background:radial-gradient(circle, rgba(244,88,31,.13), rgba(244,88,31,0) 62%);
}

/* Content has to sit above all three.

   Only the static content wrapper is touched. A blanket rule on every
   direct child would hand `position:relative` to .hero-img, .hero-foot
   and .scroll-cue, which are all absolutely positioned — that collapses
   the hero into a small box in the corner. The absolute children carry
   their own z-index already and paint above the decoration. */
.hero > .w,
.px > .w,
.pd > .w{ position:relative; z-index:1; }

/* --- hero vignette --------------------------------------------------- */
.hero-img::before{
  content:"";
  position:absolute; inset:0;
  z-index:1;
  pointer-events:none;
  background:radial-gradient(ellipse 90% 80% at 60% 45%, transparent 35%, rgba(11,22,38,.55) 100%);
}


/* ============================================================
   11. GHOST NUMERALS

   The row number now exists twice: the small orange label that
   gives the reading order, and an outlined numeral set large
   behind the row. It is the same device as the outlined figures
   on the home page, borrowed for the product and service rows.

   Drawn from data-num, so there is no extra markup to keep in
   sync and nothing for a screen reader to announce twice.
   ============================================================ */
.prow{ isolation:isolate; }
.prow::before{
  content:attr(data-num);
  position:absolute;
  top:50%; right:2%;
  transform:translateY(-50%);
  z-index:-1;
  font-family:var(--d);
  font-weight:900;
  font-size:clamp(90px,13vw,190px);
  line-height:.8;
  letter-spacing:-.06em;
  color:transparent;
  -webkit-text-stroke:1px rgba(255,255,255,.07);
  pointer-events:none;
  user-select:none;
  transition:-webkit-text-stroke-color .6s var(--e), transform .9s var(--e);
}
.prow:hover::before{
  -webkit-text-stroke-color:rgba(244,88,31,.3);
  transform:translateY(-50%) translateX(-10px);
}
/* On the ivory services section the stroke has to darken instead. */
.light .prow::before{ -webkit-text-stroke-color:rgba(18,35,63,.07); }
.light .prow:hover::before{ -webkit-text-stroke-color:rgba(244,88,31,.28); }


/* ============================================================
   12. CARDS
   A clipped corner borrowed from the logo geometry, a warmer
   surface, and a lift that behaves like paper rather than a box
   sliding upwards.
   ============================================================ */
.card{
  clip-path:polygon(0 0, calc(100% - 22px) 0, 100% 22px, 100% 100%, 0 100%);
  transition:transform .5s var(--e), background-color .5s var(--e), box-shadow .5s var(--e);
}
.card:hover{
  transform:translateY(-7px);
  box-shadow:0 22px 46px -22px rgba(11,22,38,.45);
}
.dark .card{
  background:linear-gradient(160deg, rgba(255,255,255,.055), rgba(255,255,255,.02));
}
.dark .card:hover{
  background:linear-gradient(160deg, rgba(255,255,255,.085), rgba(255,255,255,.03));
  box-shadow:0 22px 46px -24px rgba(0,0,0,.6);
}

/* The number gets a rule that draws itself, so the eye lands there first. */
.card .n{ position:relative; padding-bottom:10px; display:inline-block; }
.card .n::after{
  content:"";
  position:absolute; left:0; bottom:0;
  width:20px; height:2px;
  background:var(--orange);
  transition:width .5s var(--e);
}
.card:hover .n::after{ width:100%; }


/* ============================================================
   13. BUTTONS AND TABLE — a little more presence
   ============================================================ */

/* A hint of gradient stops the orange reading as a flat swatch, and
   the glow on hover makes the primary action feel live. */
.bt-o, .quote{
  background-image:linear-gradient(135deg, #F76A2E 0%, var(--orange) 55%, #E04A12 100%);
  transition:box-shadow .45s var(--e);
}
.bt-o:hover, .quote:hover{
  box-shadow:0 10px 30px -10px rgba(244,88,31,.6);
}

/* Rows respond to the pointer, which matters on a table this wide:
   it keeps the eye on one load class while reading across. */
.tbl tbody tr{ transition:background-color .35s var(--e); }
.tbl tbody tr:hover{ background:rgba(255,255,255,.035); }
.tbl tbody tr:hover td:first-child{ color:var(--orange); }


/* ============================================================
   14. HERO SCROLL CUE
   A hairline that fills, pauses and resets — a quiet suggestion
   that there is more below, without an animated arrow.
   ============================================================ */
.scroll-cue{
  position:absolute;
  left:var(--pad); bottom:calc(var(--pad) + 74px);
  z-index:3;
  display:flex; align-items:center; gap:14px;
  font-family:var(--d); font-weight:600;
  font-size:9.5px; letter-spacing:.24em; text-transform:uppercase;
  color:rgba(255,255,255,.45);
  pointer-events:none;
}
.scroll-cue i{
  display:block;
  width:58px; height:1px;
  background:rgba(255,255,255,.2);
  position:relative;
  overflow:hidden;
}
.scroll-cue i::after{
  content:"";
  position:absolute; inset:0;
  background:var(--orange);
  transform:scaleX(0);
  transform-origin:left center;
  animation:cue 2.6s var(--e) infinite;
}
@keyframes cue{
  0%   { transform:scaleX(0); transform-origin:left center }
  45%  { transform:scaleX(1); transform-origin:left center }
  55%  { transform:scaleX(1); transform-origin:right center }
  100% { transform:scaleX(0); transform-origin:right center }
}
@media (max-width:1100px){ .scroll-cue{ display:none } }

/* The cue is pinned a fixed distance off the bottom of the hero, but the hero
   is viewport-height: on a short screen the headline, paragraph and buttons
   stack downwards until "SCROLL" is sitting on top of the "View products"
   button. Measured overlapping at a 695px viewport, which is an ordinary
   1366x768 laptop once browser chrome is taken off.

   The cue is decoration and the buttons are the point of the page, so below
   the height where both fit, the cue goes. */
@media (max-height:860px){ .scroll-cue{ display:none } }


/* ============================================================
   15. TEXTURE LAYER — reduced motion and print
   ============================================================ */
@media (prefers-reduced-motion: reduce){
  .scroll-cue i::after{ animation:none; transform:scaleX(1) }
  .prow::before{ transition:none }
}

@media print{
  body::after,
  .hero::before, .px::before, .pd::before,
  .px::after, .pd::after,
  .hero-img::before,
  .prow::before,
  .scroll-cue{ display:none !important; }
  .card{ clip-path:none !important; box-shadow:none !important; }
  .bt-o, .quote{ background-image:none !important; }
}

/* ============================================================
   16. SIDE RAIL — without the mark

   The hexagon has been removed from the rail; the top bar
   already carries the lock-up and the link home, so the corner
   was showing the same mark twice.

   The rail was laid out with space-between across three items.
   With two left, the name would ride up to the very top, so the
   name is centred over the full height instead and the language
   switch is pinned to the foot.
   ============================================================ */
.rail{ justify-content:center; }
.rail-b{
  position:absolute;
  left:0; right:0; bottom:22px;
}

/* ============================================================
   17. TOP BAR AND RAIL

   The bar now runs the full width of the window and carries the
   logo and wordmark permanently. The rail starts underneath it,
   so the top-left corner belongs to the logo instead of being
   split between two elements.

   --bar is the bar's height. It shrinks when the bar sticks, and
   the rail's top follows it, so the two always meet with no gap
   and no overlap. enhance.js mirrors the .stuck class onto the
   body, which is what lets a class on the header change a value
   the rail reads.
   ============================================================ */
:root{ --bar:92px; }
body.bar-stuck{ --bar:76px; }

/* Full width: the bar owns the corner now. */
.top{ left:0; }

/* And the rail begins below it. */
.rail{
  top:var(--bar);
  transition:top .5s var(--e);
}

/* The progress hairline spans the whole window with the bar. */
.sprog{ left:0; }

/* --- the lock-up -----------------------------------------------------
   Always visible. The reference faded it in only once the bar stuck,
   which left the home page with no company name at all above the fold. */
.top .lock{
  opacity:1;
  transform:none;
  pointer-events:auto;
}

/* Two marks stacked, cross-faded with the bar's state. */
.lock-mark{
  position:relative;
  display:block;
  width:36px; height:36px;
  flex:none;
}
/* The two stacked <img> layers that used to cross-fade here are gone —
   the mark is one inline SVG now, and it does not change on scroll. */

/* Wordmark colours follow the same switch. Over the hero it has to be
   ivory; on the ivory bar it has to be navy. */
.top .lock .n1{ color:var(--ivory); transition:color .45s var(--e); }
.top .lock .n2{ color:rgba(244,242,237,.6); transition:color .45s var(--e); }
body.bar-stuck .top .lock .n1{ color:var(--navy); }
body.bar-stuck .top .lock .n2{ color:var(--grey); }

/* On narrow screens the wordmark would crowd the burger, so only the
   mark stays. */
@media (max-width:560px){
  .top .lock span:not(.lock-mark){ display:none; }
}

@media print{
  .rail{ top:0; }
}


/* --- full-bleed heroes ----------------------------------------------
   The page is inset by var(--rail) to clear the rail. That was fine
   while the rail ran the whole height, but now that it starts below
   the bar, the top-left gutter had nothing in it and showed the ivory
   page background beside the logo.

   Rather than patching that corner with a coloured block — which made
   the corner and the rail read as one unbroken column from the very
   top — the heroes are pulled out to the window edge and padded back
   in. The bar's transparent area now shows the hero across its whole
   width, and the rail starts below it, sitting on top of the hero.
   Its start is then unmistakable. */
.hero,
.phero{
  margin-left:calc(var(--rail) * -1);
  padding-left:var(--rail);
}

/* The hero footer is positioned against the hero's padding box, so it
   has to be pushed back in by hand or the figures drift out of line
   with the headline above them. */
.hero-foot{ left:var(--rail); }

/* A defined top edge, so the rail reads as starting rather than as
   being clipped by whatever sits above it. */
.rail{ border-top:1px solid rgba(255,255,255,.09); }

/* ============================================================
   18. ACCESSIBILITY FIXES FROM THE AUDIT
   ============================================================ */

/* --- tap targets ----------------------------------------------------
   The EN / ع switch measured 16x17px. That is fine for a mouse and far
   too small for a thumb, and the rail is on screen at every width.
   Negative margins absorb the added padding, so the hit area grows
   without anything moving. */
.rail-lang{ gap:0; }
.rail-lang a{
  padding:10px 12px;
  margin:-4px -12px;
  min-width:44px;
  text-align:center;
}

/* --- skip link ------------------------------------------------------
   White on the brand orange measures 3.35:1, under the 4.5:1 that
   12px text needs. This one is a utility control rather than brand
   surface, so it takes the navy treatment and passes comfortably.

   The positioning is also replaced. site.css hides it with
   left:-9999px, which is invisible in a left-to-right page because
   browsers do not make room to scroll before the inline start. In
   Arabic the inline start is the right-hand edge, so the same
   declaration puts the link 9999px past the END of the line and every
   RTL page gained 10,304px of empty horizontal scroll — measured at a
   320px viewport, where the page should not scroll sideways at all.

   Clipping it instead hides it identically in both directions and
   takes no space in either. */
.skip{
  background:var(--navy-d);
  color:var(--ivory);
  border:2px solid var(--orange);

  left:auto;
  right:auto;
  width:1px;
  height:1px;
  padding:0;
  margin:-1px;
  overflow:hidden;
  clip-path:inset(50%);
  white-space:nowrap;
}

/* Tabbing to it brings it back, anchored to whichever edge the reading
   direction starts at. */
.skip:focus{
  inset-inline-start:0;
  width:auto;
  height:auto;
  padding:14px 22px;
  margin:0;
  overflow:visible;
  clip-path:none;
}

/* --- honeypot -------------------------------------------------------
   The spam trap hid itself the same way the skip link did, and carried
   the same fault: left:-9999px is harmlessly off-screen in English, but
   in Arabic the inline start is the right edge, so it sat 9999px past
   the end of the line and gave the contact page a horizontal scrollbar
   into nothing.

   site.css marks every one of those declarations !important, so each
   has to be answered in kind. Clipping hides it identically in both
   directions and occupies no space in either. */
.hp{
  left:auto!important;
  right:auto!important;
  clip-path:inset(50%)!important;
  white-space:nowrap;
}


/* ============================================================
   19. NO SIDE RAIL

   The rail is gone. Rather than hunting down every rule that
   offsets something by its width — the body padding, the top
   bar, the mobile sheet, the hero bleed, the hero footer, the
   progress bar, the skip link, and their RTL counterparts —
   --rail is set to zero and they all resolve themselves.

   site.css narrows --rail to 56px under 1100px, so that has to
   be overridden at the same breakpoint. This file loads after
   site.css, so equal specificity is enough.
   ============================================================ */
:root{ --rail:0px; }
@media (max-width:1100px){ :root{ --rail:0px; } }

/* The element is no longer rendered; these guard against a stale
   cached page still carrying the markup. */
.rail{ display:none; }

/* The bleed rules become no-ops at --rail:0, but the heroes should
   not carry a stray negative margin either way. */
.hero,
.phero{ margin-left:0; padding-left:0; }
.hero-foot{ left:0; }


/* ============================================================
   20. LOGO AND WORDMARK — larger

   The mark was 36px, sized to sit quietly beside a rail that
   also carried the company name. With the rail gone this is the
   only place the brand appears above the fold, so it grows to
   fill the bar properly and shrinks with the bar when it sticks.

   54px was still too small. This mark carries a two-tone ring, a
   hexagon, two diagonal bands and two letters; at 54px the bands
   land under a pixel and the whole thing turns to mush, which is
   the same at 54px whether it is drawn from the vector or from
   the old PNG. It is a legibility floor, not a rendering fault.
   72px is the size at which every part of it survives.

   The bar has to grow to hold it. Nothing is keyed to the old
   height: --bar is read only by the scroll-restoration script,
   for its anchor offset, and it picks up the new value on its
   own. The heroes clear 104px and 130px respectively, so both
   still sit below the taller bar.
   ============================================================ */
.top{ height:var(--bar); }
.top.stuck{ height:var(--bar); }

.top .lock{ gap:16px; }

.lock-mark{
  width:72px;
  height:72px;
  transition:width .45s var(--e), height .45s var(--e);
}
.top .lock img{
  width:100%;
  height:100%;
}
body.bar-stuck .lock-mark{
  width:60px;
  height:60px;
}

.top .lock .n1{
  font-size:16px;
  letter-spacing:-.015em;
  transition:font-size .45s var(--e), color .45s var(--e);
}
.top .lock .n2{
  font-size:9px;
  letter-spacing:.2em;
  margin-top:4px;
  transition:font-size .45s var(--e), color .45s var(--e);
}
body.bar-stuck .top .lock .n1{ font-size:14px; }
body.bar-stuck .top .lock .n2{ font-size:8px; }


/* ============================================================
   21. LANGUAGE SWITCH IN THE BAR

   Moved out of the rail. Sized as a real tap target from the
   start rather than being padded back up afterwards.
   ============================================================ */
.tlang{
  display:flex;
  align-items:center;
  gap:2px;
  font-family:var(--d);
  font-weight:600;
  font-size:11px;
  letter-spacing:.12em;
}
.tlang a{
  padding:10px 8px;
  min-width:34px;
  text-align:center;
  color:rgba(244,242,237,.55);
  transition:color .35s var(--e);
}
.tlang a:hover{ color:var(--ivory); }
.tlang a[aria-current]{ color:var(--orange); }
.tlang-sep{ color:rgba(244,242,237,.25); }

/* On the ivory bar the switch has to darken with everything else. */
body.bar-stuck .tlang a{ color:var(--grey); }
body.bar-stuck .tlang a:hover{ color:var(--navy); }
body.bar-stuck .tlang a[aria-current]{ color:var(--orange); }
body.bar-stuck .tlang-sep{ color:rgba(18,35,63,.25); }

@media (max-width:560px){
  .tlang{ font-size:10px; }
  .tlang a{ padding:10px 6px; min-width:30px; }
}

/* ============================================================
   22. CARDS WITHOUT NUMBERS

   The 01 / 02 / 03 counters have been dropped from the card
   grids. The heading carried a top margin that only made sense
   with a number above it, so it closes up when it is first.

   The rule that used to draw under the number is left in place
   but inert — it only matches when a .n is present, which is
   still the case if a number is passed back in.
   ============================================================ */
.card h3:first-child{ margin-top:0; }

/* ============================================================
   23. LOGO — was: compensating for baked-in padding

   Removed. It cropped the container and scaled the <img> by
   1.29 to cancel the 22% of transparent padding baked into the
   old PNG. The mark is vector now and fills its box exactly, so
   there is nothing to cancel — and the overflow:hidden this
   section set would have clipped the SVG.
   ============================================================ */

/* ============================================================
   24. THE FIGURES BAND — rebuilt

   Two things were wrong.

   The bug: site.css styles the label with `.fgi span`, which
   also matches `<span class="num">` holding the number. The
   number was inheriting the label's 10.5px uppercase grey and
   its top border, while the `+` — an <em>, so unmatched — kept
   the full 110px. That is the tiny number above a giant plus.
   The reference build in Ref/ has the same fault.

   The design: even once the number is the right size, a 110px
   `+` carries as much weight as the figure it qualifies, four
   outlined numerals in a row read as decoration rather than
   fact, and the four cells float with nothing separating them.

   So: solid numerals — a statistics band should be legible at a
   glance, not a texture — a `+` reduced and raised to the role
   of a superscript, hairline rules framing the row, and a
   divider between each cell.
   ============================================================ */

/* --- undo the label styling that leaked onto the number --- */
.fgi b .num{
  display:inline;
  margin:0;
  padding:0;
  border-top:0;
  font-family:inherit;
  font-weight:inherit;
  font-size:inherit;
  line-height:inherit;
  letter-spacing:inherit;
  text-transform:none;
  color:inherit;
}

/* --- the row --- */
.fig-in{
  border-top:1px solid rgba(18,35,63,.14);
  border-bottom:1px solid rgba(18,35,63,.14);
  gap:0;
}

.fgi{
  padding:clamp(30px,3.6vw,52px) clamp(18px,2.2vw,38px);
  position:relative;
}
.fgi:first-child{ padding-left:0; }

/* Divider between cells, inset from the rules top and bottom. */
.fgi + .fgi::after{
  content:"";
  position:absolute;
  left:0;
  top:clamp(24px,3vw,42px);
  bottom:clamp(24px,3vw,42px);
  width:1px;
  background:rgba(18,35,63,.14);
}

/* --- the number --- */
.fgi b{
  display:flex;
  align-items:flex-start;
  font-size:clamp(46px,6.4vw,92px);
  line-height:.9;
  letter-spacing:-.045em;
  color:var(--navy);
  -webkit-text-stroke:0;
  transition:color .45s var(--e);
}

/* The plus qualifies the figure, so it reads as a superscript
   rather than a second numeral. */
.fgi b em{
  font-size:.42em;
  line-height:1;
  margin-left:.06em;
  transform:translateY(.22em);
  color:var(--orange);
}

/* --- the label --- */
.fgi span:not(.num){
  margin-top:clamp(14px,1.4vw,20px);
  padding-top:0;
  border-top:0;
  font-size:10.5px;
  letter-spacing:.2em;
  color:var(--grey);
  position:relative;
  padding-left:22px;
}
/* A short orange tick that grows when the figure is hovered. */
.fgi span:not(.num)::before{
  content:"";
  position:absolute;
  left:0;
  top:.62em;
  width:14px;
  height:2px;
  background:var(--orange);
  transition:width .5s var(--e);
}
.fgi:hover span:not(.num)::before{ width:22px; }
.fgi:hover b{ color:var(--orange); }

/* --- on the dark sections, if the band is ever used there --- */
.dark .fig-in{
  border-top-color:rgba(255,255,255,.14);
  border-bottom-color:rgba(255,255,255,.14);
}
.dark .fgi + .fgi::after{ background:rgba(255,255,255,.14); }
.dark .fgi b{ color:var(--ivory); }
.dark .fgi span:not(.num){ color:var(--grey-l); }

@media (max-width:760px){
  .fgi{ padding-inline:0; }
  .fgi:nth-child(odd){ padding-left:0; }
  .fgi + .fgi::after{ display:none; }
  .fig-in{ gap:0 clamp(16px,4vw,30px); }
}

/* ============================================================
   25. THE YEARS BADGE

   It was navy type laid straight onto the photograph. The photo
   underneath is a dark, high-contrast manhole cover, so the
   numeral fought the texture behind it and the small "YEARS"
   label all but vanished. Large type over a busy image with no
   separation is uncomfortable to read no matter how big it is.

   It becomes a plate: solid navy, the numeral in ivory, the
   plus in orange, and the corner cut on the same angle as the
   cards. The figure no longer competes with the photograph, it
   sits on top of it — and because the plate carries its own
   background, contrast is fixed at every image the editor might
   swap in later.

   Smaller, too. At 132px it was shouting; on a plate it reads
   at half that.
   ============================================================ */
.ab-num{
  right:-5%;
  bottom:6%;
  z-index:2;

  display:inline-block;
  padding:clamp(16px,1.6vw,22px) clamp(20px,2vw,28px) clamp(14px,1.4vw,18px);

  background:var(--navy);
  color:var(--ivory);

  /* Same clipped corner as the cards, from the logo geometry. */
  clip-path:polygon(16px 0, 100% 0, 100% 100%, 0 100%, 0 16px);
  box-shadow:0 22px 44px -20px rgba(11,22,38,.6);

  font-size:clamp(40px,4.6vw,68px);
  line-height:.86;
  letter-spacing:-.04em;
}

.ab-num em{
  color:var(--orange);
  font-size:.62em;
  line-height:1;
  margin-left:.04em;
  display:inline-block;
  transform:translateY(-.34em);
}

.ab-num span{
  margin-top:10px;
  font-size:9.5px;
  letter-spacing:.24em;
  color:rgba(244,242,237,.62);
  white-space:nowrap;
}

/* The orange bloom behind the hexagon sits at the top-left; the plate
   is bottom-right, so the two frame the image rather than collide. */

@media (max-width:1100px){
  .ab-num{ right:0; bottom:-3%; }
}
@media (max-width:760px){
  .ab-num{
    font-size:clamp(34px,9vw,48px);
    padding:14px 18px 12px;
  }
}

/* ============================================================
   26. NO SERIAL NUMBERING

   Every 01 / 02 / 03 is gone from the site: the small orange
   label above product and service headings, the large outlined
   numeral behind those rows, the counters in the two project
   listings, and the leading number on the products page labels.

   Both listings had the number as a real grid cell, so the
   column tracks have to lose one each or the remaining content
   sits in the wrong place.
   ============================================================ */

/* Projects page index: was 64px | title | supplied | arrow */
.irow{ grid-template-columns:minmax(0,1fr) auto auto; }

/* Home showcase: was 52px | thumb | body | arrow.
   The thumbnail is display:none above 1000px, so only two
   tracks are needed there. */
.pshow-row{ grid-template-columns:minmax(0,1fr) auto; }

/* The ghost numeral drew from data-num, which no longer exists. */
.prow::before{ content:none; }

@media (max-width:1100px){
  .irow{ grid-template-columns:minmax(0,1fr) auto; }
  .irow .m{ grid-column:1 / -1; }
}
@media (max-width:1000px){
  .pshow-row{ grid-template-columns:92px minmax(0,1fr) auto; }
}
@media (max-width:760px){
  .pshow-row{ grid-template-columns:64px minmax(0,1fr); }
}

/* ============================================================
   27. LOGO — was: sized by layout, not by transform

   Removed with section 23. Both existed only to undo the old
   PNG's padding; there is no <img> in the lock-up any more.
   ============================================================ */

/* ============================================================
   29. THE MARK

   Geometry comes from the vector master in Ref/pack/svg/ via
   templates/partials/logo.php. Colour comes from here.

   The defect being fixed: the ring is two arcs — 270 degrees of
   orange and a 90 degree minor arc across the top. Put the minor
   arc's colour against a background of the same value and it
   renders perfectly at about 1.05:1, which reads as a ring
   broken between 10 and 2. It is a colourway problem, so the
   fix is picking the right colourway — never a filter, blend
   mode, drop-shadow or backing plate.

   The thing that took me three wrong attempts to see: this mark
   carries its own disc. That disc is the mark's background, so
   the bar behind it is irrelevant — it is always sitting on a
   light plate, and it therefore always wants the light
   colourway, with the minor arc in navy. Serving the dark
   colourway here inverts the logo: the navy disc disappears
   into the navy bar and the hexagon reads ivory, which is the
   negative of the real mark.

   That is also why the original site never swapped this asset
   on scroll. It does not need to.
   ============================================================ */
.hm-mark{
  display:block;
  width:100%;
  height:100%;
  overflow:visible;
}

/*
  Painted as fallbacks rather than as properties on .hm-mark itself:
  set on the element they would beat anything an ancestor declares, and
  the mark could never be retinted by the context it sits in. As
  fallbacks any wrapper can override a single colour.

  Default is the mark as drawn — white disc, orange ring, navy minor
  arc, navy hexagon, ivory letters. The ring closes because the navy
  arc is on the white disc, not on the bar.
*/
.hm-disc{ fill:var(--logo-disc, var(--white));  transition:fill .4s var(--e); }
.hm-ring{ fill:var(--logo-ring, var(--orange)); }
.hm-arc { fill:var(--logo-arc,  var(--navy));   transition:fill .4s var(--e); }
.hm-hex { fill:var(--logo-hex,  var(--navy));   transition:fill .4s var(--e); }
.hm-text{ fill:var(--logo-ink,  var(--ivory));  transition:fill .4s var(--e); }

/*
  The footer panel is navy and the lockup there is deliberately quieter,
  so it drops the disc and inverts: the minor arc and hexagon go ivory so
  the ring still closes against the navy, and the letters go navy. This
  is the one place the mark reverses, and it reverses as a whole rather
  than half of it disappearing.
*/
.lock-lg .hm-mark{
  --logo-disc:transparent;
  --logo-arc:var(--ivory);
  --logo-hex:var(--ivory);
  --logo-ink:var(--navy);
  width:74px; height:74px; flex:0 0 74px;
}
@media (max-width:700px){
  .lock-lg .hm-mark{ width:58px; height:58px; flex:0 0 58px; }
}

/* Nothing animates on scroll any more, but a context that does retint
   the mark should not do it abruptly for someone who asked for less
   motion. */
@media (prefers-reduced-motion:reduce){
  .hm-disc,.hm-arc,.hm-hex,.hm-text{ transition:none; }
}

/* The container no longer holds two stacked images, so the sizing
   compensation that cancelled the old PNG padding is not needed: the
   vector already fills its box. */
.lock-mark{
  overflow:visible;
  position:relative;
}
.top .lock .lock-mark svg{
  position:static;
  width:100%;
  height:100%;
  max-width:none;
  transform:none;
  inset:auto;
}


/* ============================================================
   30. MOBILE

   Written against real viewports rather than a resized desktop
   window: the site was loaded at 320, 375 and 768 in same-origin
   iframes, which give media queries a genuine viewport, and the
   numbers below come from measuring inside them.

   Horizontal overflow was already clean at every width — the
   only elements crossing the viewport edge are the skip link
   parked off-screen, the oversized hero photograph the parallax
   needs, and the marquee, all of which are meant to.

   What was actually wrong: the bar and the tap targets.
   ============================================================ */

/* --- the mobile menu could not be closed -----------------------------
   The burger turns into an X when the sheet opens, so it is meant to be
   the close control, and it carries z-index:101 to sit above the sheet's
   96. It never did.

   #top has z-index:90, which makes it a stacking context, so the burger's
   101 is only 101 *within the bar*. The whole bar — burger included — is
   therefore pinned below the sheet. elementFromPoint over the burger
   returns #sheet, so the click never reaches it.

   Nothing else closes the menu: there is no close button inside the
   sheet, no backdrop to tap, and no Escape handler. Once a visitor opened
   the menu on a phone the only way out was to navigate to a page. This
   was true in both languages.

   Raising the bar is what actually lifts the burger, since the burger
   cannot escape its parent's context. The background is dropped at the
   same time so the sheet reads as one uninterrupted panel with the logo
   and the X floating on it, rather than having a bar stripe across the
   top of it. */
.top:has(#burg.on){
  z-index:98;
  background:transparent;
  box-shadow:none;
  backdrop-filter:none;
}

/* --- the hero -------------------------------------------------------
   The worst thing on the mobile site, and it hid the two buttons the page
   exists to get pressed.

   .hero-foot — the 16+ / 50+ / 1,000+ / 100+ band — is absolutely
   positioned against the bottom of the hero. On a desktop that is safe,
   because .hero is min-height:100svh and there is empty space down there
   for it to sit in. site.css drops the hero to min-height:auto at this
   same breakpoint, so the hero becomes exactly as tall as its text and
   the band ends up on top of that text instead of below it.

   Measured at 375px: hero 479px tall, the band starting at y=296 and
   running 182px. The intro paragraph was cut off mid-sentence and both
   calls to action — "The range" and "Request a quote" — sat completely
   behind it, visible only as two faint orange shapes bleeding through
   the band's translucent background.

   Putting the band back in normal flow fixes it without touching the
   desktop layout: the hero becomes a plain block that stacks headline,
   paragraph, buttons, then band. */
@media (max-width:760px){
  .hero{
    display:block;
    padding-bottom:0;
  }
  .hero-foot{
    position:static;
    margin-top:34px;
  }
}

/* --- the bar ---------------------------------------------------------
   72px of mark in a 92px bar is desktop sizing. On a 320px screen that
   is 23.6% of the width, and the bar alone eats an eighth of the
   viewport height.

   Shrinking it does not bring back the mush that made the mark too
   small on desktop: that was measured at DPR 1. A phone renders these
   same CSS pixels at 2x or 3x, so 52px on a handset is 104-156 device
   pixels — more than the 72px desktop mark gets. */
@media (max-width:700px){
  :root{ --bar:72px; }
  body.bar-stuck{ --bar:60px; }

  .lock-mark{ width:52px; height:52px; }
  body.bar-stuck .lock-mark{ width:44px; height:44px; }
}

@media (max-width:400px){
  :root{ --bar:64px; }
  body.bar-stuck{ --bar:56px; }

  .lock-mark{ width:46px; height:46px; }
  body.bar-stuck .lock-mark{ width:40px; height:40px; }

  /* Sub-page headlines were being cut off on the narrowest phones.
     .phero h1's clamp bottoms out at 34px, and a word like DISTINGUISHED
     or SPECIFICATION — fourteen characters of 900-weight uppercase — needs
     more width than the 265px content column of a 320px screen. Measured
     there: /about wanted 280px and got 265, so the last letters simply
     vanished. /services and /contact lost 4px each.

     It reported as no overflow because .phero clips, so the page never
     gained a scrollbar to give the problem away — the text was just
     quietly missing.

     Letting the words break is not the fix: the headline's lines are
     pre-split into .l wrappers that each hide their own overflow for the
     reveal, so a wrapped word would drop out of sight instead of moving
     to a second line. The type has to get smaller.

     30px at 320 and back to the original 34px by 360 — narrow enough to
     fit, and unchanged on every screen that was already fine. */
  .phero h1{ font-size:clamp(30px, 9.4vw, 34px); }
}

/* --- tap targets -----------------------------------------------------
   Measured on a real 320px viewport, these were all under the 44px
   minimum a fingertip needs:

     EN / ع            30 x 37
     footer nav links  various x 17
     "See all projects" 167 x 26

   Each is fixed by growing the hit area, not the type — the design's
   proportions stay as they are. */
@media (max-width:900px){

  /* The switch was 30x37. Padding alone would push the two apart, so the
     box grows around the label instead and the label stays centred. */
  .tlang a{
    min-width:44px;
    min-height:44px;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    padding:0 6px;
  }

  /* Footer links were 17px tall because the list item carried the only
     spacing and the anchor was inline. Making the anchor a block and
     moving the gap into it means the whole row is tappable. */
  .ft li{ margin-bottom:0; }
  .ft li a{
    display:block;
    padding:11px 0;
    line-height:1.4;
  }

  .pshow-all{
    padding-top:12px;
    padding-bottom:16px;
  }

  /* Tap-to-call is the most useful control on the page on a handset, and it
     was the smallest thing on it — 121x17 inside the footer's <address>.
     inline-block rather than block so the <br> rhythm around it is
     unchanged. WhatsApp is the same case: 240x15, and on a phone it is the
     control most likely to be used.

     .wa is excluded because this is a rule about growing *text links* that
     were too small to hit, and the floating button is neither: it is
     already 54x54, and matching it here broke it. It is display:grid with
     place-items:center to hold the icon in the middle of the hexagon;
     inline-block landed on it, blockified to block by position:fixed —
     which throws the centring away — and then padding:14px 2px pushed the
     24px icon into the top-left corner of a 54px shape, where the
     hexagon's sloped edge cuts towards it. Measured at 390px: the icon
     sat at 2,14 instead of 15,15. Below 900px only, so the button was
     centred on a desktop and off-centre on every phone. */
  a[href^="tel:"]:not(.wa),
  a[href^="https://wa.me/"]:not(.wa),
  a[href^="mailto:"]:not(.wa){
    display:inline-block;
    padding:14px 2px;
    line-height:1.2;
  }

  /* The breadcrumb is 10px uppercase, so its anchors were 11px tall. The
     type stays as designed; only the hit area grows. */
  .crumb a{
    display:inline-block;
    padding:12px 4px;
    margin:-12px -4px;
  }
}


/* ============================================================
   THE PRODUCT CATALOGUE
   ------------------------------------------------------------
   Under each family section on the products page: the actual
   item list, broken into headed runs. The family above it is
   the argument; this is the shelf.

   Cards are deliberately quieter than .card - no lift, no
   orange underline - because a family section can hold thirteen
   of them and thirteen hovering tiles is noise.
   ============================================================ */
.cat{margin-top:clamp(40px,5vw,72px)}

.cat-h{
  font-family:var(--d);
  font-weight:700;
  font-size:clamp(15px,1.5vw,19px);
  letter-spacing:-.01em;
  color:var(--navy);
  margin:clamp(34px,3.6vw,52px) 0 clamp(16px,1.6vw,22px);
  padding-bottom:12px;
  border-bottom:1px solid rgba(18,35,63,.14);
}
.cat-h:first-child{margin-top:0}
.dark .cat-h{color:var(--ivory);border-bottom-color:rgba(255,255,255,.14)}

.cat-grid{
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(14px,2vw,26px);
}

.cat-c{
  background:var(--white);
  display:flex;
  flex-direction:column;
  overflow:hidden;
}
.sec-b .cat-c{background:var(--white)}

/* A fixed ratio, because the source photographs run from square
   product renders to wide crops and a ragged grid reads as broken.
   object-fit does the rest. */
.cat-i{
  aspect-ratio:4/3;
  background:var(--ivory);
  overflow:hidden;
}
.cat-i img{
  width:100%;
  height:100%;
  object-fit:cover;
  transition:transform .7s var(--e);
}
.cat-c:hover .cat-i img{transform:scale(1.04)}

.cat-b{padding:clamp(16px,1.8vw,24px)}
.cat-k{
  font-family:var(--d);
  font-weight:800;
  font-size:10px;
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--orange);
  margin:0 0 8px;
}
.cat-b h4{
  font-family:var(--d);
  font-weight:700;
  font-size:clamp(14px,1.25vw,17px);
  letter-spacing:-.01em;
  line-height:1.3;
  color:var(--navy);
  margin:0;
}
.cat-b p{
  font-size:13.5px;
  line-height:1.65;
  color:var(--grey);
  margin:10px 0 0;
}

/* An item with no photograph - the two mechanical castings - would
   otherwise sit shorter than its neighbours in the same row. */
.cat-c:not(:has(.cat-i)) .cat-b{padding-top:clamp(22px,2.4vw,30px)}

@media (max-width:1080px){
  .cat-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:640px){
  .cat-grid{grid-template-columns:1fr}
  .cat-i{aspect-ratio:16/9}
}


/* ============================================================
   WHAT A SERVICE INCLUDES
   ------------------------------------------------------------
   The bullet list under a service row. Same hex marker as
   .ab-list, but without the rules between items: these run to
   five short lines and the borders were heavier than the text.
   ============================================================ */
.rlist{
  list-style:none;
  margin:18px 0 0;
  padding:0;
  display:grid;
  gap:9px;
}
.rlist li{
  position:relative;
  padding-inline-start:20px;
  font-size:14px;
  line-height:1.6;
  color:var(--grey);
}
.rlist li::before{
  content:"";
  position:absolute;
  inset-inline-start:0;
  top:.55em;
  width:8px;
  height:8px;
  background:var(--orange);
  clip-path:var(--hex);
}
.dark .rlist li{color:var(--grey-l)}


/* ============================================================
   THE QUESTIONS ON THE CONTACT PAGE
   ------------------------------------------------------------
   <details>, so it works with JavaScript off and the browser
   handles the keyboard for us. The marker is replaced by a
   drawn one that rotates, since the native triangle cannot be
   styled consistently and points the wrong way in RTL.
   ============================================================ */
.faq{
  border-top:1px solid rgba(18,35,63,.14);
  max-width:88ch;
}
.faq-i{border-bottom:1px solid rgba(18,35,63,.14)}

.faq-i summary{
  list-style:none;
  cursor:pointer;
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:24px;
  padding:clamp(18px,1.9vw,26px) 0;
  font-family:var(--d);
  font-weight:600;
  font-size:clamp(15px,1.4vw,18px);
  letter-spacing:-.01em;
  color:var(--navy);
  transition:color .3s var(--e);
}
.faq-i summary::-webkit-details-marker{display:none}
.faq-i summary:hover{color:var(--orange)}

.faq-i summary::after{
  content:"";
  flex:0 0 auto;
  width:11px;
  height:11px;
  border-right:2px solid var(--orange);
  border-bottom:2px solid var(--orange);
  transform:rotate(45deg);
  margin-block-start:-5px;
  transition:transform .4s var(--e);
}
.faq-i[open] summary::after{
  transform:rotate(-135deg);
  margin-block-start:3px;
}

.faq-i p{
  margin:0;
  padding:0 0 clamp(20px,2.2vw,28px);
  max-width:70ch;
  font-size:15px;
  line-height:1.8;
  color:var(--grey);
}

/* The reveal animation sets opacity on .rv; a <details> that is
   still closed must not animate its hidden answer. */
.faq-i summary:focus-visible{outline:2px solid var(--orange);outline-offset:3px}


/* ============================================================
   PROJECT CARDS
   ------------------------------------------------------------
   The projects page reuses the catalogue card (.cat-c) so the
   two pages read as one system. These are the project-only
   touches: a location badge sitting on the photo, and a title
   that lifts on hover. A project with no photo falls back to
   the plain text card the catalogue already styles.
   ============================================================ */
.pj-c .cat-i{aspect-ratio:16/11}
.pj-c h4{transition:color .3s var(--e)}
.pj-c:hover h4{color:var(--orange)}

.pj-loc{
  position:absolute;
  inset-block-end:10px;
  inset-inline-start:10px;
  z-index:1;
  font-family:var(--d);
  font-weight:700;
  font-size:10px;
  letter-spacing:.12em;
  text-transform:uppercase;
  color:#fff;
  background:rgba(11,22,38,.72);
  backdrop-filter:blur(3px);
  padding:5px 10px;
}
.pj-c .cat-i{position:relative}

/* The location line inside a text-only project card (no photo). */
.pj-meta{
  font-size:13px;
  font-family:var(--d);
  font-weight:600;
  letter-spacing:.02em;
  color:var(--grey);
  margin:10px 0 0;
}

/* A text-only project card should not look empty next to photo
   cards: give it a hairline top accent in the brand orange. */
.pj-c:not(:has(.cat-i)){border-top:3px solid var(--orange)}


/* ============================================================
   31. THE MOBILE MENU — rebuilt

   What was there: six links and the language switch, centred
   vertically in a panel that cannot scroll, the switch being a
   single orange word carrying an inline style.

   Measured at 390x760 with the real navigation, the stack ran
   from 170px to the bottom edge and the language link sat hard
   against it. At 760px of viewport it fits by a few pixels.
   Below that — a 360x640 handset, or any phone in landscape —
   the last rows are simply unreachable, because .sheet had no
   overflow rule and justify-content:center pushes what does not
   fit out of both ends at once.

   Three other things were wrong with it:

     - The panel is clipped, not display:none, so its seven
       links stayed in the tab order at every width while it was
       shut. site.js now carries `inert` for that.

     - The bar sits on top of the panel and its colours follow
       the scroll position. Open the menu anywhere below the
       fold and body.bar-stuck is still set, so the wordmark
       renders navy on the panel's near-black — 1.3:1, which is
       to say gone. Same for the language switch beside it.

     - .quote is display:none below 760px and was not in the
       panel either, so the one control the site exists to get
       pressed did not exist on a phone.

   Laid out as a panel now: pages, then the language pair, then
   a foot that holds the quote button and the two direct
   contact routes. It scrolls if it has to, and it stops the
   page behind it from scrolling with it.
   ============================================================ */
.sheet{
  display:block;
  overflow-y:auto;
  overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  padding:0;
}

.sheet-in{
  min-height:100%;
  display:flex;
  flex-direction:column;
  gap:clamp(16px,3.4vw,26px);
  /* Clears the bar at the top and the home indicator at the bottom. --bar
     already tracks the breakpoints, so this follows it down. */
  padding:calc(var(--bar) + clamp(14px,3vw,26px)) var(--pad)
          calc(clamp(18px,4vw,30px) + env(safe-area-inset-bottom, 0px));
}

/* --- the pages ------------------------------------------------------- */
.sheet-nav{
  display:flex;
  flex-direction:column;
}
.sheet-nav a{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:14px;
  font-family:var(--d);
  font-weight:800;
  font-size:clamp(21px,5.6vw,32px);
  line-height:1.1;
  letter-spacing:-.025em;
  text-transform:uppercase;
  padding:clamp(11px,2.6vw,15px) 0;
  border-block-end:1px solid rgba(244,242,237,.11);
  opacity:0;
  transform:translateY(14px);
  transition:opacity .45s var(--e), transform .45s var(--e), color .3s var(--e);
}
.sheet.on .sheet-nav a{
  opacity:1;
  transform:none;
}
.sheet-nav a:last-child{ border-block-end:0; }

/* The page you are on. The skewed block is the same marker the section
   tags use, so the panel is not inventing a device of its own. */
.sheet-nav a.on{ color:var(--orange); }
.sheet-nav a.on::after{
  content:"";
  flex:none;
  width:26px;
  height:8px;
  background:var(--orange);
  transform:skewX(-31.5deg);
}

/* --- the language pair ------------------------------------------------
   Both languages, each in its own name, the current one filled in. One
   orange word gave no clue that a second language existed, which way it
   would switch, or that it was a control at all. */
.sheet-lang,
.sheet-foot{
  opacity:0;
  transform:translateY(14px);
  transition:opacity .45s var(--e), transform .45s var(--e);
}
.sheet.on .sheet-lang,
.sheet.on .sheet-foot{
  opacity:1;
  transform:none;
}

.sheet-lbl{
  font-family:var(--d);
  font-weight:600;
  font-size:10px;
  letter-spacing:.28em;
  text-transform:uppercase;
  color:var(--orange);
  margin:0 0 11px;
}
.sheet-langs{
  display:flex;
  flex-wrap:wrap;
  gap:10px;
}
.sheet-langs a,
.sheet-langs span{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  min-height:46px;
  padding:12px 20px;
  font-family:var(--d);
  font-weight:700;
  font-size:13px;
  letter-spacing:.08em;
  color:var(--ivory);
  border:1px solid rgba(244,242,237,.24);
  transition:background .3s var(--e), border-color .3s var(--e), color .3s var(--e);
}
.sheet-langs .on{
  background:var(--orange);
  border-color:var(--orange);
  color:#fff;
}
.sheet-langs a:hover{ border-color:var(--ivory); }

/* Arabic set in Latin type is the first thing a reader of it notices. */
.sheet-langs [lang="ar"]{
  font-family:var(--ar);
  letter-spacing:0;
}

/* --- the foot --------------------------------------------------------- */
.sheet-foot{
  margin-block-start:auto;
  padding-block-start:clamp(14px,3vw,20px);
  border-block-start:1px solid rgba(244,242,237,.11);
  display:flex;
  flex-direction:column;
  align-items:stretch;
  gap:6px;
}
/* The panel used to be a flat list of <a>, and site.css styles those as a
   block: `.sheet a` sets 7vw display type, its own padding, and negative
   tracking. Those declarations still land on the parts of the rebuilt panel
   that are anchors but are not navigation.

   The quote button is where it shows. It is a .bt, and .bt is (0,1,0) while
   `.sheet a` is (0,1,1), so the legacy rule wins every property the two
   share. Measured at 390px the button came out at 27.3px with 10px 0
   padding — more than twice the 12px every other button on the site uses,
   and with the button's own horizontal padding overridden to nothing. That
   is what makes it read as oversized rather than merely full width.
   `.sheet-nav a` is (0,2,0), which is why the navigation above it was
   correct and this was not.

   Scoping under .sheet lifts this to (0,2,0) and hands the button back its
   own type, so it matches the buttons in the hero and on the contact page. */
.sheet .sheet-cta{
  font-family:var(--d);
  font-weight:700;
  font-size:12px;
  letter-spacing:.16em;
  text-transform:uppercase;
  line-height:1.2;
  padding:18px 28px;
  justify-content:center;
}
.sheet-reach{
  display:flex;
  flex-wrap:wrap;
  gap:2px clamp(18px,5vw,30px);
}
.sheet-reach a{
  font-family:var(--d);
  font-weight:600;
  font-size:13px;
  letter-spacing:.05em;
  color:var(--grey-l);
  transition:color .3s var(--e);
}
.sheet-reach a:hover{ color:var(--ivory); }

/* --- the bar while the panel is open ----------------------------------
   Raising the bar above the panel is what lets the burger be clicked at
   all (see section 30). Everything below is about what the bar looks
   like once it is up there: it is over near-black now, whatever the
   scroll position says, so it takes the light colourway back.

   body.menu-open is set by site.js. The :has() rule in section 30 stays
   for the z-index, which has to hold even if the class is not set. */
body.menu-open .top,
.top:has(#burg.on){
  z-index:98;
  background:transparent;
  box-shadow:none;
  backdrop-filter:none;
}
body.menu-open .top .lock .n1{ color:var(--ivory); }
body.menu-open .top .lock .n2{ color:rgba(244,242,237,.6); }

/* The panel carries a full-sized language control of its own, so the
   cramped pair in the bar would be a second control for the same thing
   sitting a few pixels from the close button. */
.tlang{ transition:opacity .25s var(--e); }
body.menu-open .tlang{
  opacity:0;
  pointer-events:none;
}

/* Same for the quote button. It survives in the bar between 760 and 1100,
   which is exactly the range where the burger is showing too — so with the
   panel open there were two Request-a-quote buttons on screen at once, the
   bar's one sitting on the panel a few pixels from the close button. */
.quote{ transition:opacity .25s var(--e); }
body.menu-open .quote{
  opacity:0;
  pointer-events:none;
}

/* Nothing behind the panel should move while it is open. */
body.menu-open{ overscroll-behavior:none; }

/* Tablets. The burger runs up to 1100px, so between here and there the
   panel is being asked to fill a window rather than a phone screen. Left
   to stretch, the rows read as a phone menu that has been pulled wide,
   and at 900x760 the stack overflowed by about twenty pixels — the type
   scales with the width, so the wider it got the less of it fitted.

   Holding the column to a readable measure fixes the look and the
   overflow at the same time. */
@media (min-width:700px){
  .sheet-in{
    max-width:560px;
    margin-inline-end:auto;
    gap:clamp(14px,2vw,20px);
  }
  .sheet-nav a{
    font-size:26px;
    padding:12px 0;
  }
  /* Scoped for the same reason as the base rule above — at (0,1,0) the
     horizontal padding lost to `.sheet a` and the button collapsed to the
     width of its text. */
  .sheet .sheet-cta{ align-self:flex-start; padding-inline:44px; }
}

/* Short viewports — a phone in landscape, or a small handset with the
   browser chrome showing. The panel scrolls, but it should need to as
   late as possible, so the type steps down before it does. */
@media (max-height:620px){
  .sheet-nav a{
    font-size:clamp(17px,4.4vw,22px);
    padding:9px 0;
  }
  .sheet-in{ gap:14px; }
  .sheet .sheet-cta{ padding-block:14px; }
}

@media (prefers-reduced-motion:reduce){
  .sheet-nav a,
  .sheet-lang,
  .sheet-foot{
    opacity:1;
    transform:none;
  }
}


/* ============================================================
   32. HERO TONE — less navy over the photograph

   Every hero photograph was carrying four darkening layers at
   once: a brightness filter on the image, a navy gradient over
   it, a vignette on top of that, and — below 760px — the whole
   thing dropped to 40% opacity over the navy section behind it.
   Multiplied together the photograph contributed under a fifth
   of what you actually saw on a phone, which is why every hero
   read as a flat navy panel with a suggestion of a building in
   it.

   The scrim is not decoration, though: the headline is ivory
   and it sits over the photograph on narrow screens, so it
   cannot simply be removed. What changes is where it is spent
   — heavily where the type is, and close to nothing over the
   rest of the frame, rather than evenly across the whole thing.
   ============================================================ */

/* --- home hero -------------------------------------------------------- */
.hero-img img{
  filter:saturate(.92) contrast(1.03) brightness(.96);
}
.hero-img::after{
  background:linear-gradient(100deg,
    var(--navy) 0%,
    rgba(18,35,63,.74) 26%,
    rgba(18,35,63,.20) 52%,
    rgba(18,35,63,.04) 100%);
}
.hero-img::before{
  background:radial-gradient(ellipse 90% 80% at 60% 45%,
    transparent 45%, rgba(11,22,38,.24) 100%);
}

/* --- sub-page hero ---------------------------------------------------- */
.phero-img img{
  filter:saturate(.9) contrast(1.03) brightness(.94);
}
.phero-img::after{
  background:linear-gradient(100deg,
    var(--navy) 2%,
    rgba(18,35,63,.76) 32%,
    rgba(18,35,63,.24) 58%,
    rgba(18,35,63,.06) 100%);
}

/* --- narrow screens ---------------------------------------------------
   Here the photograph is full-bleed behind the type rather than beside
   it, so the gradient turns vertical: it holds behind the headline and
   the paragraph and lets go over the lower half of the frame.

   object-position does the other half of the work. These photographs are
   skylines — sky on top, city below — and the sky is the brightest part
   of the frame, which is the worst possible thing to put behind ivory
   type. Pulling the crop down puts the mid-tone city behind the words
   instead, so the scrim over it can be much lighter than it would need
   to be over the sky. The 40% opacity wash is gone. */
@media (max-width:760px){
  .hero-img,
  .phero-img{ opacity:1; }

  .hero-img img,
  .phero-img img{
    object-position:50% 80%;
    filter:saturate(.9) contrast(1.05) brightness(.86);
  }

  .hero-img::after{
    background:linear-gradient(180deg,
      rgba(11,22,38,.82) 0%,
      rgba(18,35,63,.68) 45%,
      rgba(18,35,63,.44) 76%,
      rgba(18,35,63,.32) 100%);
  }

  /* The ghost button is a hairline and a word. Beside the solid orange
     one on a plain navy panel that is enough; over a city skyline it
     stops reading as a button at all — the border disappears into the
     roofline. It gets its own ground back, only where it needs it. */
  .hero .bt-g{
    background:rgba(11,22,38,.5);
    border-color:rgba(255,255,255,.42);
    backdrop-filter:blur(3px);
  }
  /* The sub-page hero is short and its text fills the whole of it, so
     unlike the home hero there is no lower half to hand back to the
     photograph. It keeps a scrim the whole way down — still a third of
     what it was carrying, but even rather than tapered. */
  .phero-img::after{
    background:linear-gradient(180deg,
      rgba(11,22,38,.78) 0%,
      rgba(18,35,63,.66) 55%,
      rgba(18,35,63,.56) 100%);
  }

  /* The vignette was tuned for a photograph sitting in the corner of a
     wide frame. Full-bleed it just darkens the edges of the screen. */
  .hero-img::before{ background:none; }

  /* With the photograph actually visible behind it, the type needs its
     own edge — a roofline or a window reveal landing behind a letter is
     what makes text over a picture hard to read, and no amount of scrim
     fixes that without putting the navy straight back. A shadow costs
     the photograph nothing: over the flat navy it is invisible, and
     over the detail it is the whole difference.

     This is why the scrim above could be set as light as it is. */
  .hero-in h1,
  .hero-p,
  .phero .w > *{
    text-shadow:0 1px 3px rgba(11,22,38,.55);
  }
  .hero-in h1{
    text-shadow:0 2px 10px rgba(11,22,38,.5);
  }
}


/* ============================================================
   33. THE FLOATING WHATSAPP BUTTON

   The centring fault is fixed where it was caused, in the tap
   target block in section 30. Two things left, both about where
   the button sits rather than what is inside it.

   Inset. The button is pinned 16px from the bottom of the
   viewport. On a handset with a home indicator that is inside
   the gesture strip — the swipe takes the press, and iOS draws
   the indicator over the button. env() is zero on every device
   without one, so this costs nothing anywhere else.

   Symmetry. site.css pins it 16px from the right; rtl.css moved
   it to the left but at 22px, tightening to 16px only below
   900px. So on an Arabic desktop the button sat six pixels
   further out than on an English one, and the value changed
   under a breakpoint in one language and not the other. The 22
   was clearing the side rail, which no longer exists.

   One value now, in a logical property, so the two languages
   mirror exactly. The physical pair is cleared first and the
   logical one declared after it, because in LTR both resolve to
   `right` and the later declaration is the one that lands. The
   two rules this replaces have been taken out of rtl.css — left
   there they would load afterwards and win.
   ============================================================ */
.wa{
  right:auto;
  left:auto;
  inset-inline-end:16px;
  inset-block-end:calc(16px + env(safe-area-inset-bottom, 0px));
}

/* --- and its size on a handset ----------------------------------------
   54px was desktop sizing, and it was the only piece of chrome that
   never stepped down: the bar goes 92 -> 72 -> 64 across these same
   breakpoints and the mark goes 72 -> 52 -> 46, while this stayed as
   drawn. At 390px it is 14% of the width of the screen, sitting on top
   of the content, which is what makes it read as oversized rather than
   merely large.

   The shape has to be handed to a pseudo-element on the way down.
   clip-path clips hit testing as well as paint, so a hexagon is already
   a smaller target than the box it is cut from — full width only across
   the middle half of its height, tapering to a point top and bottom.
   Shrinking a clipped shape therefore shrinks the target twice over,
   and section 30 of this file spent real effort getting everything on a
   phone up to 44px. Cutting the plate rather than the button keeps the
   whole square tappable at the size the eye sees a smaller badge.

   The icon is positioned so it paints over the plate: ::before is
   absolutely positioned and would otherwise cover a static sibling. */
@media (max-width:700px){
  .wa{
    width:48px;
    height:48px;
    background:transparent;
    clip-path:none;
    inset-inline-end:14px;
    inset-block-end:calc(14px + env(safe-area-inset-bottom, 0px));
  }
  .wa::before{
    content:"";
    position:absolute;
    inset:0;
    background:var(--orange);
    clip-path:var(--hex);
  }
  .wa svg{
    position:relative;
    width:21px;
    height:21px;
  }
}

@media (max-width:400px){
  .wa{
    width:44px;
    height:44px;
    inset-inline-end:12px;
    inset-block-end:calc(12px + env(safe-area-inset-bottom, 0px));
  }
  .wa svg{
    width:19px;
    height:19px;
  }
}


/* ============================================================
   33. HOME HERO — retuned for the Abdali Boulevard photograph

   Section 32 was written against a dusk skyline: dark sky on
   top, mid-tone city below, and its scrim thins out from 45%
   down because there was nothing bright under there to fight.

   The photograph is now a sunlit boulevard — blue sky, glass
   facades, white tensile canopies and a lit walkway. Measured
   at 430px, the intro paragraph lands directly on the canopy,
   which is the brightest thing in the frame, and #A7B4C8 on
   white is not a readable pair.

   Note that object-position's vertical term does nothing here.
   The hero box on a phone is roughly square and the picture is
   4:3, so `cover` scales to the height and crops the sides —
   the full height is always shown. Only the horizontal term is
   live, which is why the crop is left centred and the work is
   done with the scrim instead.
   ============================================================ */
@media (max-width:760px){
  .hero-img img{
    filter:saturate(.88) contrast(1.05) brightness(.80);
  }

  /* Held a little further down than section 32 left it — through
     the headline and the paragraph — then released over the
     bottom of the frame, where nothing is written and the
     walkway can still be seen.

     Deliberately restrained. Carrying the whole legibility fix
     in the scrim takes about .90 flat, and at that weight the
     photograph stops being a photograph, which is the exact
     failure section 32 was written to undo. */
  .hero-img::after{
    background:linear-gradient(180deg,
      rgba(11,22,38,.86)  0%,
      rgba(11,22,38,.78) 40%,
      rgba(18,35,63,.66) 64%,
      rgba(18,35,63,.44) 86%,
      rgba(18,35,63,.30) 100%);
  }

  /* The rest of the contrast is bought locally instead of
     globally. The headline is 900-weight ivory and already held
     its own; it was only the paragraph that broke up over the
     white canopy, so the paragraph is the only thing that gets
     a step brighter and a shadow tight enough to separate it
     from whatever it lands on. This keeps the picture. */
  .hero-p{
    text-shadow:0 1px 3px rgba(11,22,38,.9);
  }
}

/* --- headline size ----------------------------------------------------
   The home headline was set to fill the left half of the hero on a wide
   screen, and at 116px it did — three lines of it, taking about 40% of
   the viewport height before the paragraph even started.

   Two reductions, and they are aimed at different places.

   First, a flat 3px off the whole clamp rather than only its ends, so
   that part is the same everywhere.

   Then the fluid term and the ceiling come down — 7.6vw to 6.2vw and
   116px to 92px — which is where the real cut is. That is about 19%
   from 768px upward and nothing at all below roughly 645px, where the
   37px floor already governs: the complaint is a desktop one, and the
   phone size was settled separately and is left where it is.

   Scoped to .hero, so the sub-page headlines keep their own sizing and
   the narrow-screen clamp in section 31 still applies to them.

   Second pass: another 24px off the two solid lines, 92 -> 68 at the
   ceiling, with the fluid term brought to 5vw to reach it smoothly. The
   37px floor is untouched, so it still governs below about 800px and the
   phone size does not move.

   The headline is set at -.05em, which pulls the word spaces in along
   with the letters and ran IRON and THAT together. word-spacing puts
   that back without loosening the letterforms themselves. */
.hero h1{
  font-size:clamp(37px, calc(5.0vw - 3px), 68px);
  word-spacing:.12em;
}

/* The outlined line is the accent, and it goes the other way: 10px up on
   what it measured before, 92 -> 102.

   Expressed as a multiple of the headline rather than as its own clamp,
   for two reasons. The two lines keep their relationship through the
   fluid middle instead of drifting apart, and it needs no breakpoint of
   its own — 1.5x holds on a phone, a tablet and a desktop alike, rather
   than applying only above 760px. 1.5 x 68 = 102 at the ceiling. */
.hero h1 .out{ font-size:1.5em; }

/* Arabic sets letter-spacing:0 on the headings (rtl.css) because the
   negative Latin tracking breaks the joins. The word-spacing above exists
   only to undo that same negative tracking, so with it already gone there
   is nothing to undo and the extra gap would just read as loose. */
[dir="rtl"] .hero h1{ word-spacing:normal; }


/* ============================================================
   34. THE BAR NEEDS ITS OWN GROUND

   .top is fixed and transparent, and site.js only gives it the
   ivory .stuck background after 60% of the viewport height has
   been scrolled. On the home page that leaves roughly 460px of
   scrolling during which the hero headline slides up underneath
   a bar that has nothing behind it — so IRON THAT crosses the
   wordmark and runs through PRODUCTS and PROJECTS.

   The transparent bar is deliberate (the hero is meant to sit
   behind it), so the fix is not to make it opaque early. It is
   to give the bar a navy scrim of its own that fades out below
   it: the menu keeps a constant ground to sit on, anything
   scrolling under it fades out rather than colliding, and the
   bar still reads as transparent over the hero because the
   scrim is the same navy the hero already is.

   It also buys something at rest — on the home page the right
   hand end of the nav sits over the bright sky of the
   photograph, and the scrim is what makes it legible there.

   Not applied once .stuck lands, because from that point the
   ivory background is doing the same job.
   ============================================================ */
.top::before{
  content:"";
  position:absolute;
  inset:0 0 auto 0;
  height:calc(100% + 34px);
  z-index:-1;
  pointer-events:none;
  opacity:1;
  transition:opacity .45s var(--e);
  /* The scrim is 34px taller than the bar, so the bar itself occupies
     roughly the top 70%. Across that band it has to be effectively
     opaque, or hero copy scrolling underneath stays legible straight
     through the menu - which is the overlap this scrim exists to stop.
     It was .92 fading to .42 by 76%, which left the paragraph plainly
     readable behind the links. It now holds until the bar ends and
     does all its fading in the 30% below, where there is nothing to
     collide with and the softness is the point. */
  background:linear-gradient(180deg,
    rgba(11,22,38,.97)  0%,
    rgba(11,22,38,.95) 62%,
    rgba(11,22,38,.92) 72%,
    rgba(11,22,38,.38) 88%,
    rgba(11,22,38,0)  100%);
}
.top.stuck::before{ opacity:0; }

/* ============================================================
   34b. THE BAR SMEARS WHEN IT SCROLLS

   site.css gives .top.stuck a backdrop-filter: blur(16px) over a
   background that is already 94% opaque, on an element that is
   position:fixed AND transitions its own height. Chromium cannot
   keep that on a single composited layer: it re-rasterises the
   whole strip every frame and leaves horizontal streaks of the
   previous frame behind it while the page moves.

   The blur was buying almost nothing at 94% opacity, so it goes.
   translateZ(0) then pins the bar to its own layer so scrolling
   underneath composites rather than repaints.

   Height stays animated: it is a 14px step that runs once, not
   per frame, and changing it would move the lock-up geometry.
   ============================================================ */
.top{
  transform:translateZ(0);
  will-change:height, background-color;
}
.top.stuck{
  backdrop-filter:none;
  -webkit-backdrop-filter:none;
  background:rgba(244,242,237,.985);
}


/* ============================================================
   35. HERO SPLIT — photograph 65%, navy panel 35%

   site.css sets .hero-img to 56% and steps it to 70% under
   1100px and 100% under 760px. Only the widescreen figure
   changes here, so the tablet and phone steps are untouched
   and the rule is scoped above 1100px rather than declared
   flat — enhance.css loads after site.css, and a flat
   .hero-img{width:65%} would out-cascade those two media
   queries and take the phone layout with it.

   The diagonal is cut from the image box, not the hero, so
   widening the image also walks the diagonal left: it now
   runs from about 54% of the hero at the top to 35% at the
   bottom, against 61% and 44% before.
   ============================================================ */
@media (min-width:1101px){
  .hero-img{ width:65%; }
}


/* ============================================================
   36. HERO INTRO — the paragraph stops looking faded

   site.css sets .hero-p to #A7B4C8, a muted grey-blue chosen
   to sit quietly under the headline. Against the navy panel it
   sits a little too quietly: the sentence that actually says
   what the business sells is the faintest text in the section.

   #CFD8E4 is the same hue, lifted. It was already being used
   below 760px, where the paragraph lands on the photograph;
   this simply applies it at every width rather than treating
   the phone as the exception.

   The colour moves, the weight and size do not — the headline
   stays the loudest thing in the hero.

   The text-shadow in section 33 stays where it was, scoped to
   narrow screens: it exists to separate the paragraph from the
   photograph behind it, and on a wide screen the paragraph is
   over the flat navy panel with nothing to separate from.
   ============================================================ */
.hero-p{ color:#CFD8E4; }


/* ============================================================
   37. THE OFFICIAL LOCK-UP IN THE BAR AND THE FOOTER

   The name used to be set in Archivo next to the mark. That was a
   good stand-in, but it is not the wordmark: the real artwork has
   its own drawn letterforms (the open-apex A, the tracked rule
   either side of TRADING ESTABLISHMENT) that type cannot imitate.

   The mark stays vector and stays in the markup, because it is
   still what a narrow screen shows — the wordmark would crowd the
   burger there. Above 560px the full artwork takes over and the
   standalone mark steps aside, so the mark is never drawn twice.

   Two colourways cross-fade with the bar, the same switch the type
   used to make: ivory over the hero, full colour once the bar goes
   ivory. A raster cannot recolour itself the way the SVG did, so
   the two files are stacked and their opacity is swapped.
   ============================================================ */
.lock-art{
  position:relative;
  display:block;
  height:72px;
  aspect-ratio:1800 / 365;
  flex:none;
  transition:height .45s var(--e);
}
.lock-art .lk{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:contain;
  transition:opacity .45s var(--e);
}
.lock-art .lk-rev{ opacity:1 }
.lock-art .lk-col{ opacity:0 }

body.bar-stuck .lock-art{ height:60px }
body.bar-stuck .lock-art .lk-rev{ opacity:0 }
body.bar-stuck .lock-art .lk-col{ opacity:1 }

/* With the sheet open the bar is dark again, whatever the scroll
   position, so the reverse artwork has to come back with it. */
body.menu-open .lock-art .lk-rev{ opacity:1 }
body.menu-open .lock-art .lk-col{ opacity:0 }

/* Above the narrow breakpoint the artwork carries its own mark. */
@media (min-width:561px){
  .top .lock .lock-mark{ display:none; }
}

/* --- footer -----------------------------------------------------------
   Always on the navy panel, so only the reverse colourway is needed. The
   Arabic name keeps its own line underneath. */
/* Scoped to .lock-lg on purpose: site.css sets `.lock-lg img{width:74px;
   height:74px}` for the square mark that used to live here, and at (0,1,1)
   that beats a bare .ft-lockup — which squashed the wide lock-up into a
   74px box. Two classes outrank it. */
.lock-lg .ft-lockup{
  display:block;
  width:auto;
  height:74px;
  max-width:100%;
}
@media (max-width:760px){
  .lock-lg .ft-lockup{ height:58px; }
}
