/* ============================================================
   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 - speed is set from JS. */
.rb-in{ animation-duration:var(--ribbon-speed, 38s) !important; }

/* 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 } }


/* ============================================================
   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. */
  a[href^="tel:"],
  a[href^="https://wa.me/"],
  a[href^="mailto:"]{
    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;
  }
}
