/* ============================================================
   SAKKARY MACHINERY — base and chrome · MIXED BANDS
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Opening the request modal locks scrolling; without a reserved
     gutter the scrollbar vanishes and the whole page jumps sideways. */
  scrollbar-gutter: stable;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, video, svg { display: block; max-width: 100%; }
img, video { height: auto; }
[hidden] { display: none !important; }

h1, h2, h3, h4 { margin: 0; font-weight: 700; line-height: var(--lh-snug); }
p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; cursor: pointer; }

:focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 3px; border-radius: var(--r-1); }
::selection { background: var(--accent-fill); color: #fff; }

/* ---------- Primitives ---------- */

.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
.section { position: relative; padding-block: var(--section-y); }
.band { background: var(--bg); color: var(--fg); }
/* .band--tint is declared in tokens.css, which loads first — without
   this the generic .band rule below it would win and repaint it white. */
.band.band--tint { background: var(--bg-alt); }

.spec {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  color: var(--fg-3);
  font-weight: 500;
}
.spec--hot  { color: var(--accent); }
.spec--cold { color: var(--cool); }

.eyebrow { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-4); }
.eyebrow::before {
  content: "";
  width: 38px; height: 2px; flex: none;
  background: var(--accent-bright);
}
.eyebrow--center { justify-content: center; }
/* The section label carries more weight than a caption — it is the
   first thing read in each band, so it sits a step above the other
   mono labels rather than at the micro size. */
.eyebrow .spec { font-size: var(--fs-xs); font-weight: 600; }

.h-display {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-3xl);
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-display);
  text-transform: uppercase;
}
.h-section {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-2xl);
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-display);
  text-transform: uppercase;
}
.lede { color: var(--fg-2); font-size: var(--fs-lg); max-width: var(--maxw-text); }
.h-display + .lede, .h-section + .lede { margin-top: var(--s-4); }

/* ============================================================
   BUTTONS — a wipe fills from the leading edge, the label rides
   above it, and on a fine pointer the button leans toward you.
   ============================================================ */

.btn {
  --btn-fg: var(--fg);
  --btn-bd: var(--line);
  --btn-wipe: var(--accent-fill);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 50px;
  padding: 0 var(--s-6);
  color: var(--btn-fg);
  background: transparent;
  border: 1px solid var(--btn-bd);
  border-radius: var(--r-1);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  overflow: hidden;
  isolation: isolate;
  will-change: transform;
  /* the magnetic offset lives in custom properties so the pressed
     scale composes with it rather than overwriting it */
  transform: translate(var(--mx, 0px), var(--my, 0px));
  transition: color var(--dur-2) var(--ease-cut),
              border-color var(--dur-2) var(--ease-cut),
              transform 320ms var(--ease-cut);
}
.btn > * { position: relative; z-index: 1; }
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  /* -1, not 0. .btn isolates, so a negative layer still paints above
     the button's own background — but below its inline content. At 0
     the wipe painted OVER a bare text label, because a positioned
     z-index:0 descendant comes after inline content in paint order.
     Only labels wrapped in an element were safe, via .btn > *. */
  z-index: -1;
  background: var(--btn-wipe);
  transform: scaleX(0);
  /* transform-origin takes no logical keywords — `inline-start` is
     invalid and the whole declaration gets dropped, leaving the
     default 50% 50%, so the wipe grew out of the middle of the button
     instead of sweeping across it. Physical values, mirrored for RTL. */
  transform-origin: left center;
  transition: transform 420ms var(--ease-cut);
}
:root[dir="rtl"] .btn::before { transform-origin: right center; }
.btn:hover::before, .btn:focus-visible::before { transform: scaleX(1); }
.btn:active { transform: translate(var(--mx, 0px), var(--my, 0px)) scale(.97); }
.btn[disabled] { opacity: .45; pointer-events: none; }

.btn--primary {
  --btn-fg: var(--accent-on);
  --btn-bd: var(--accent-fill);
  --btn-wipe: #B3121F;
  background: var(--accent-fill);
}
.btn--primary:hover { --btn-bd: #B3121F; }

.btn--ghost { --btn-bd: var(--line); }
.btn--ghost:hover, .btn--ghost:focus-visible { --btn-fg: var(--accent-on); --btn-bd: var(--accent-fill); }

.btn__ico { width: 16px; height: 16px; flex: none; transition: transform var(--dur-2) var(--ease-cut); }
.btn:hover .btn__ico--arrow { transform: translateX(4px); }
:root[dir="rtl"] .btn__ico--arrow { transform: scaleX(-1); }
:root[dir="rtl"] .btn:hover .btn__ico--arrow { transform: scaleX(-1) translateX(4px); }

/* ============================================================
   CURSOR — an arrow that tracks exactly and a ring that trails it.
   White + difference blending so one cursor reads on the white
   bands, the graphite bands and the photography alike.
   Only ever enabled on a fine pointer, by JS.
   ============================================================ */

.cursor { position: fixed; inset: 0; pointer-events: none; z-index: var(--z-cursor); display: none; }
html.has-cursor .cursor { display: block; }
html.has-cursor,
html.has-cursor a,
html.has-cursor button,
html.has-cursor label,
html.has-cursor input,
html.has-cursor select,
html.has-cursor textarea { cursor: none; }

/* Brand blue, lifted just enough to clear 4:1 against BOTH the white
   bands and the graphite ones — a blended white cursor vanished on
   the white sections. Red takes over when you are over something
   clickable, so the two brand colours carry the two pointer states. */
.cursor__dot, .cursor__ring {
  position: absolute;
  top: 0; left: 0;
  will-change: transform;
}
.cursor__ring { border-radius: 50%; }
/* The inner mark is a pointer arrow rather than a decorative dot, so the
   cursor reads the way an operating-system pointer does while the ring
   keeps the site's own character. clip-path draws the classic arrow
   silhouette; its tip sits exactly on the pointer position, which is why
   this one carries no centring margin the way the ring does. The white
   drop-shadow is what keeps it legible over the dark hero and the white
   bands alike. */
.cursor__dot {
  width: 13px; height: 19px;
  margin: 0;
  background: var(--cursor-c);
  clip-path: polygon(0 0, 0 89%, 35% 68%, 57% 98%, 80% 91%, 58% 62%, 100% 61%);
  filter: drop-shadow(0 0 1px rgba(255, 255, 255, .95));
  transition: opacity var(--dur-2), background var(--dur-2);
}
.cursor__ring {
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border: 1.5px solid var(--cursor-c);
  transition: width var(--dur-3) var(--ease-cut),
              height var(--dur-3) var(--ease-cut),
              margin var(--dur-3) var(--ease-cut),
              opacity var(--dur-2),
              border-color var(--dur-2);
}
.cursor[data-state="link"] .cursor__dot  { background: var(--cursor-hot); }
.cursor[data-state="link"] .cursor__ring { border-color: var(--cursor-hot); }
/* over something clickable the ring opens up and the dot gets out of the way */
.cursor[data-state="link"] .cursor__ring { width: 58px; height: 58px; margin: -29px 0 0 -29px; }
/* The arrow stays visible over links — it is the pointer, and hiding it
   at the moment of clicking is what made the old dot feel arbitrary.
   The ring alone carries the hover state. */
.cursor[data-state="text"] .cursor__ring { width: 20px; height: 20px; margin: -10px 0 0 -10px; opacity: .5; }
.cursor[data-state="down"] .cursor__ring { width: 26px; height: 26px; margin: -13px 0 0 -13px; }
.cursor[data-hidden="true"] .cursor__dot,
.cursor[data-hidden="true"] .cursor__ring { opacity: 0; }

/* While a modal is open the pointer goes back to the system one.
   `revert` rather than `auto` so the UA stylesheet still supplies the
   I-beam over inputs and the pointer over links and buttons — a blanket
   `auto` would flatten all of them to a plain arrow. */
html.cursor-off .cursor { display: none; }
html.has-cursor.cursor-off,
html.has-cursor.cursor-off input,
html.has-cursor.cursor-off textarea { cursor: revert; }
/* Buttons get `default` from the UA stylesheet, not `pointer` — inside
   a form that reads as "not clickable", so state it. */
html.has-cursor.cursor-off a,
html.has-cursor.cursor-off button,
html.has-cursor.cursor-off label,
html.has-cursor.cursor-off select,
html.has-cursor.cursor-off summary { cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  html.has-cursor .cursor { display: none; }
  html.has-cursor, html.has-cursor * { cursor: auto; }
}

/* ---------- Header ----------
   It sits over the dark hero to begin with, so it starts light-on-dark
   and repaints to the page's own palette once it sticks. */

.header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--z-header);
  height: var(--header-h);
  display: flex;
  align-items: center;
  color: #F2F4F7;
  border-bottom: 1px solid transparent;
  transition: background var(--dur-3) var(--ease-cut),
              color var(--dur-3) var(--ease-cut),
              border-color var(--dur-3) var(--ease-cut);
}
/* No backdrop-filter here on purpose. The header sticks after 24px of
   scroll, which is exactly while the hero video is still on screen and
   playing — so a blur would re-filter a full-width strip of moving
   video on every frame. At 96% opacity the blur was contributing almost
   nothing visually and costing a great deal. */
.header[data-stuck="true"] {
  background: rgba(255, 255, 255, .96);
  border-bottom-color: var(--line);
  color: #14161A;
}
.header__in {
  width: 100%; max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex; align-items: center; gap: var(--s-5);
}

/* The brand is the real logo file — the same lockup the footer carries
   — not a mark beside "SAKKARY" typed in the site's own fonts, which is
   what used to sit here and did not match the artwork anywhere it
   appeared next to it.

   Two files, one shown at a time: the header is transparent
   light-on-dark until it sticks and white dark-on-light after, and an
   SVG loaded through <img> cannot inherit currentColor, so the wordmark
   ink has to come from the file. Identical artwork otherwise, and the
   second one is cached from the first page on. */
.brand { display: flex; align-items: center; flex: none; }
.brand__logo { width: 121px; height: 46px; }
/* The logo is the header's home link. At 40px on a phone it was the
   last thing up there under the 44pt minimum; the header is 74px tall,
   so the box can simply grow around the artwork. */
@media (pointer: coarse) { .brand { min-height: 44px; } }
.brand__logo--dark { display: none; }
.header[data-stuck="true"] .brand__logo--light { display: none; }
.header[data-stuck="true"] .brand__logo--dark  { display: block; }
/* An open drawer puts the header back over a dark scrim, so the light
   wordmark comes back with it — matching the colour flip the header
   already does a few rules below. */
:root.drawer-open .header[data-stuck="true"] .brand__logo--light { display: block; }
:root.drawer-open .header[data-stuck="true"] .brand__logo--dark  { display: none; }

.nav { display: flex; align-items: center; gap: var(--s-1); margin-inline-start: auto; }
.nav__link {
  position: relative;
  padding: var(--s-2) var(--s-3);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: .08em;
  text-transform: uppercase;
  opacity: .78;
  transition: opacity var(--dur-2) var(--ease-cut);
}
.nav__link::after {
  content: "";
  position: absolute;
  inset-inline: var(--s-3);
  bottom: 2px; height: 1px;
  background: var(--accent-bright);
  transform: scaleX(0);
  transform-origin: inline-start;
  transition: transform var(--dur-2) var(--ease-cut);
}
.nav__link:hover { opacity: 1; }
/* Any aria-current value, not just "true": the home page marks its own
   nav entry aria-current="page" (the correct token for a navigation
   location) and the content pages emitted "true". Matching the
   attribute rather than one of its values means the underline shows
   either way, and both are now "page". */
.nav__link:hover::after, .nav__link[aria-current]::after { transform: scaleX(1); }
.nav__link[aria-current] { opacity: 1; }

.header__act { display: flex; align-items: center; gap: var(--s-3); flex: none; }
.header__cta { min-height: 42px; padding-inline: var(--s-5); }

.lang { display: inline-flex; border: 1px solid currentColor; border-radius: var(--r-1); opacity: .55; transition: opacity var(--dur-2); }
.lang:hover { opacity: 1; }
.lang__b {
  /* Explicit box, because this is a <button> on the home page and an
     <a> on the blog (where switching language is a different URL, not
     a CSS toggle). A button centres its label on its own; an inline
     anchor ignores min-width and min-height entirely and sits on the
     baseline. Same rule, same result either way. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 42px; min-height: 34px;
  padding: 0 var(--s-2);
  background: transparent; border: 0;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .1em;
  line-height: 1;
  text-decoration: none;
  color: inherit; opacity: .7;
}
.lang__b[aria-pressed="true"],
.lang__b[aria-current="true"] { opacity: 1; background: rgba(128, 128, 128, .22); }

/* On a touch screen 42×34 is under the 44pt minimum, and this is the
   control that switches the site between English and Arabic — not
   somewhere to make people aim. Height only: the two buttons share an
   edge with no gap, so widening them would overlap their hit areas.
   The 74px header absorbs the extra 10px, and the pill ends up
   matching the 44px burger beside it. The drawer copy is already 44. */
@media (pointer: coarse) {
  .lang__b { min-height: 44px; }
}

.burger {
  display: none;
  width: 44px; height: 44px;
  align-items: center; justify-content: center;
  background: transparent;
  border: 1px solid currentColor;
  border-radius: var(--r-1);
  opacity: .8;
}
.burger__bars { width: 18px; height: 12px; position: relative; }
.burger__bars i {
  position: absolute; inset-inline: 0; height: 1.5px; background: currentColor;
  transition: transform var(--dur-2) var(--ease-cut), opacity var(--dur-1);
}
.burger__bars i:nth-child(1) { top: 0; }
.burger__bars i:nth-child(2) { top: 5px; }
.burger__bars i:nth-child(3) { top: 10px; }
.burger[aria-expanded="true"] i:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.burger[aria-expanded="true"] i:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] i:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

/* ---------- Drawer ---------- */

.drawer {
  position: fixed; inset: 0;
  z-index: var(--z-drawer);
  background: #0B0C0F;
  color: #F2F4F7;
  padding: calc(var(--header-h) + var(--s-6)) var(--gutter) var(--s-7);
  display: flex; flex-direction: column; gap: var(--s-2);
  opacity: 0; visibility: hidden; transform: translateY(-8px);
  transition: opacity var(--dur-3) var(--ease-cut),
              transform var(--dur-3) var(--ease-cut),
              visibility var(--dur-3);
}
.drawer[data-open="true"] { opacity: 1; visibility: visible; transform: none; }
.drawer__link {
  padding: var(--s-4) 0;
  border-bottom: 1px solid rgba(255,255,255,.1);
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  text-transform: uppercase;
}
.drawer__link:hover { color: #F46B85; }
.drawer .btn { margin-top: var(--s-5); --btn-bd: rgba(255,255,255,.24); }
.drawer__lang { margin-top: var(--s-5); align-self: flex-start; opacity: 1; }
.drawer__lang .lang__b { min-height: 44px; min-width: 56px; }

/* While the drawer is open the header floats over a dark overlay, so
   it drops its light background and goes light-on-dark to match. */
:root.drawer-open .header,
:root.drawer-open .header[data-stuck="true"] {
  background: transparent;
  backdrop-filter: none;
  border-bottom-color: transparent;
  color: #F2F4F7;
}

/* 1200, not 1080. The nav gained a Home link, and eight items plus the
   brand, the language pill and the CTA no longer fit the old 1081px
   threshold. Measured at 1201: 1116px needed against 1186px available,
   so 70px of slack in English and 104px in Arabic, whose labels are
   shorter. (It was 1155px needed until the brand became the real logo
   file, which is 39px narrower than the mark-plus-typed-name it
   replaced — the margin is comfortable now rather than tight.) Below
   this the burger takes over, and the drawer carries every destination
   the nav does, so nothing becomes unreachable. */
@media (max-width: 1200px) {
  .nav { display: none; }
  .header__act { margin-inline-start: auto; }
  .burger { display: inline-flex; }
  /* The language toggle stays out in the open on phones — switching
     to Arabic should not require opening a menu first. */
}
@media (max-width: 720px) { .header__cta { display: none; } }
@media (max-width: 420px) {
  .header__in { gap: var(--s-3); }
  .lang__b { min-width: 36px; }
  /* The lockup comes down a step rather than dropping its strapline —
     it is one image now, so there is no strapline to drop. */
  .brand__logo { width: 105px; height: 40px; }
}
@media (min-width: 1201px) { .drawer { display: none; } }

/* ---------- Scroll progress: a hairline across the very top ---------- */
.progress {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  height: 2px;
  z-index: calc(var(--z-header) + 1);
  background: transparent;
  pointer-events: none;
}
.progress__bar {
  height: 100%;
  width: var(--p, 0%);
  background: linear-gradient(90deg, var(--blue-deep), var(--red));
}
:root[dir="rtl"] .progress__bar { margin-inline-start: auto; }

/* ============================================================
   REVEALS — progressive enhancement, as in the other builds
   ============================================================ */

.cut-in { position: relative; display: inline-block; }
:root.js-anim .cut-in { clip-path: inset(0 100% 0 0); transition: clip-path 600ms var(--ease-cut); }
:root.js-anim[dir="rtl"] .cut-in { clip-path: inset(0 0 0 100%); }
:root.js-anim .cut-in.is-in { clip-path: inset(0 0 0 0); }
:root.js-anim .cut-in.cut-done { clip-path: none; transition: none; }
:root.js-anim .cut-in::after {
  content: "";
  position: absolute;
  inset-block: -5%;
  inset-inline-start: -1px;
  width: 2px;
  background: var(--accent-bright);
  opacity: 0;
}
:root.js-anim .cut-in.is-in::after { animation: edge-run 600ms var(--ease-cut) forwards; }
:root.js-anim .cut-in.cut-done::after { display: none; }
@keyframes edge-run {
  0%   { opacity: 1; inset-inline-start: 0; }
  90%  { opacity: 1; inset-inline-start: 100%; }
  100% { opacity: 0; inset-inline-start: 100%; }
}

:root.js-anim .rise {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 460ms var(--ease-cut), transform 460ms var(--ease-cut);
  transition-delay: var(--d, 0ms);
}
:root.js-anim .rise.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .cut-in { clip-path: none !important; }
  .cut-in::after { display: none !important; }
  .rise { opacity: 1 !important; transform: none !important; transition: none; }
  .btn { transform: none !important; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ---------- Boot ---------- */

.boot {
  position: fixed; inset: 0;
  z-index: var(--z-modal);
  display: grid; place-items: center;
  background: #0B0C0F;
  transition: opacity 600ms var(--ease-cut), visibility 600ms;
}
/* pointer-events drops the instant the loader is done, without waiting
   on the fade. A transition that never completes — throttled tab,
   backgrounded load, compositor asleep — would otherwise leave a
   full-screen overlay swallowing every tap on the page. */
.boot[data-done="true"] { opacity: 0; visibility: hidden; pointer-events: none; }
.boot__in { display: flex; flex-direction: column; align-items: center; gap: var(--s-5); }
.boot__mark { width: 46px; height: 64px; }
.boot__bar { width: min(220px, 46vw); height: 1px; background: rgba(255,255,255,.16); position: relative; }
.boot__bar::after {
  content: "";
  position: absolute; inset-block: 0; inset-inline-start: 0;
  width: var(--boot, 0%);
  background: var(--red);
  transition: width 220ms var(--ease-cut);
}
.boot__pct {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-mono);
  color: #7E858F;
}

/* ---------- Skip link ---------- */
.skip {
  position: absolute;
  inset-inline-start: var(--s-4);
  top: -80px;
  z-index: var(--z-modal);
  padding: var(--s-3) var(--s-4);
  background: var(--accent-fill);
  color: #fff;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  border-radius: var(--r-1);
  transition: top var(--dur-2) var(--ease-cut);
}
.skip:focus { top: var(--s-4); }

:root[data-lang="en"] .lang-ar,
:root[data-lang="ar"] .lang-en { display: none !important; }

/* ---------- floating actions + a11y helper ----------
   Moved here from sections.css: these are global chrome, present
   on every server-rendered page through the shared request-form
   partial, but sections.css is only loaded by the home page. With
   no .wa-fab rule the button's <svg viewBox="0 0 20 20"> has no
   size constraint and expands to the full page width. */

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.wa-fab {
  position: fixed;
  inset-block-end: max(18px, env(safe-area-inset-bottom));
  inset-inline-end: 18px;
  z-index: var(--z-header);
  width: 56px; height: 56px;
  display: grid; place-items: center;
  background: #128C7E;
  color: #fff;
  border: 0;
  border-radius: 50%;
  box-shadow: 0 6px 20px -6px rgba(18, 140, 126, .7), var(--shadow-2);
  transition: transform var(--dur-2) var(--ease-cut), background var(--dur-2);
}
.wa-fab:hover, .wa-fab:focus-visible { transform: scale(1.07); background: #0F7A6D; }
.wa-fab:active { transform: scale(.96); }
.wa-fab svg { width: 26px; height: 26px; position: relative; z-index: 1; }

/* A slow expanding ring — enough to draw the eye back every few
   seconds without nagging. */
.wa-fab__pulse {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: #25D366;
  opacity: .55;
  animation: wa-pulse 2.8s var(--ease-cut) infinite;
}
@keyframes wa-pulse {
  0%        { transform: scale(1);    opacity: .5; }
  70%, 100% { transform: scale(1.85); opacity: 0; }
}
.wa-fab:hover .wa-fab__pulse { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) { .wa-fab__pulse { display: none; } }

/* ---------- Back to top ----------
   Mirrors the WhatsApp bubble on the opposite side. Both use logical
   inset properties, so in Arabic they swap together and stay opposite
   rather than landing on top of each other. */
.top-fab {
  position: fixed;
  inset-block-end: max(18px, env(safe-area-inset-bottom));
  inset-inline-start: 18px;
  z-index: var(--z-header);
  width: 52px; height: 52px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .94);
  color: #14161A;
  border: 1px solid var(--line);
  border-radius: 50%;
  box-shadow: var(--shadow-2);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--dur-3) var(--ease-cut),
              transform var(--dur-3) var(--ease-cut),
              background var(--dur-2), color var(--dur-2);
}
.top-fab[data-show="true"] { opacity: 1; transform: none; }
.top-fab:hover, .top-fab:focus-visible { background: var(--accent-fill); color: #fff; border-color: var(--accent-fill); }
.top-fab:active { transform: scale(.95); }
.top-fab svg { width: 22px; height: 22px; }
@media (max-width: 520px) { .top-fab { width: 46px; height: 46px; } .top-fab svg { width: 20px; height: 20px; } }

/* ---------- client logo wall ----------
   Shared: the home page band and the /about/ page both render it,
   and only the home page loads sections.css. Same reason .wa-fab
   lives here. */

.logos {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-2);
  overflow: hidden;
}
@media (max-width: 760px) { .logos { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.logos__cell {
  background: var(--surface);
  aspect-ratio: 16 / 9;
  display: grid;
  place-items: center;
  padding: var(--s-4);
  text-align: center;
  transition: background var(--dur-2) var(--ease-cut);
}
.logos__cell:hover { background: var(--surface-2); }

/* Sixteen logos with wildly different proportions have to look like
   one set. Each carries its own --lh, sized by aspect ratio, so a long
   wordmark and a round emblem read at the same optical weight rather
   than the same pixel height. Grey at rest keeps sixteen competing
   palettes from shouting over the page; colour arrives on hover. */
.logos__cell img {
  max-width: 82%;
  max-height: var(--lh, 46px);
  width: auto;
  height: auto;
  object-fit: contain;
  /* Grayscale alone is not enough: half these marks are gold, light
     blue or thin outlines, and desaturating them just leaves them
     pale. Pulling brightness down first lands every logo in the same
     mid-grey band regardless of its original colour, while keeping
     internal detail that a flat silhouette would destroy. */
  filter: var(--f, grayscale(1) brightness(.62) contrast(1.3));
  opacity: .78;
  transition: filter var(--dur-3) var(--ease-cut), opacity var(--dur-3) var(--ease-cut);
}
.logos__cell:hover img { filter: none; opacity: 1; }
@media (max-width: 760px) { .logos__cell img { max-height: calc(var(--lh, 46px) * .82); } }

.clients__tally {
  margin-top: var(--s-6);
  display: flex;
  align-items: baseline;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--s-3);
  text-align: center;
}
.logos__n {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2rem, 1.4rem + 2.2vw, 3.25rem);
  line-height: 1;
  letter-spacing: var(--tr-display);
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

/* ============================================================
   FOOTER — graphite, anchoring every page

   Moved here from sections.css when the full footer replaced the slim
   one on the content and blog pages. sections.css is loaded by the home
   page alone, so a footer rule left in it would style the home page and
   silently do nothing everywhere else — the same trap that once blew the
   WhatsApp button up to full page width on /ar/training/.
   ============================================================ */

.foot { padding-block: var(--s-9) var(--s-6); }
.foot__top {
  display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.6fr);
  gap: var(--s-8);
  padding-bottom: var(--s-7);
  border-bottom: 1px solid var(--line);
}
.foot__mark { margin-bottom: var(--s-5); }
.foot__tag { margin-bottom: var(--s-3); }
.foot__est { color: var(--fg-3); }
.foot__cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr)); gap: var(--s-6); }
.foot__h { margin-bottom: var(--s-4); color: var(--accent); }
.foot__row { display: flex; align-items: flex-start; gap: var(--s-3); margin-bottom: var(--s-4); color: var(--fg-2); font-size: var(--fs-sm); }
.foot__row svg { width: 17px; height: 17px; flex: none; color: var(--fg-3); margin-top: 3px; }
.foot__row a:hover { color: var(--accent); }
.foot__links { display: grid; gap: var(--s-2); font-size: var(--fs-sm); }
.foot__links a { color: var(--fg-2); }
.foot__links a:hover { color: var(--fg); }
/* On a phone this list is eight stacked links and the thumb has to hit
   one of them. A 22px line of text is not a tap target; padding takes
   each row past the 44px minimum without changing the desktop rhythm,
   where the pointer is precise and the tighter list reads better. */
@media (max-width: 900px) {
  .foot__links { gap: 0; }
  .foot__links a { display: block; padding: var(--s-3) 0; }
  /* The phone, WhatsApp, email and map links render as a 24px line of
     text. They are the footer's whole point on a phone, so they get the
     same treatment; the row margin comes down by what the padding adds,
     which keeps the column's rhythm where it was. */
  .foot__row { margin-bottom: var(--s-2); }
  .foot__row a { display: inline-block; padding-block: 10px; }
}
.foot__bot { padding-top: var(--s-5); display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-4); }
.foot__legal { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2) .6em; }

.social { display: flex; gap: var(--s-2); }
.social a {
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--r-1);
  color: var(--fg-3);
  transition: color var(--dur-2), border-color var(--dur-2);
}
.social a:hover { color: var(--fg); border-color: var(--accent-bright); }
.social svg { width: 19px; height: 19px; }
@media (max-width: 900px) { .foot__top { grid-template-columns: 1fr; } }

/* ---------- search form ----------
   Shared by the home hero and the /search results page, which is why
   it lives here and not beside either of them: index.html loads
   main.css + sections.css, chrome.php loads main.css + pages.css, and
   main.css is the only stylesheet both of them have. The same rule in
   either of the other two would style one surface and silently vanish
   on the other.

   Every colour is a semantic token, so .band--dark redefining them is
   all it takes for the field to sit correctly on the graphite hero. */
.sform { display: flex; gap: var(--s-3); flex-wrap: wrap; max-width: 560px; }
.sform__i {
  flex: 1 1 240px;
  min-width: 0;
  min-height: 48px;
  padding: var(--s-3) var(--s-4);
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: var(--r-1);
  font-family: var(--font-body);
  font-size: var(--fs-base);
  transition: border-color var(--dur-2) var(--ease-cut), box-shadow var(--dur-2) var(--ease-cut);
}
.sform__i::placeholder { color: var(--fg-3); opacity: .8; }
.sform__i:hover { border-color: var(--fg-3); }
.sform__i:focus {
  outline: none;
  border-color: var(--accent-bright);
  box-shadow: 0 0 0 3px rgba(234, 35, 54, .14);
}
/* Icon only, so square rather than a text button's 0/32px padding —
   50px matches .btn's min-height and clears the 44px touch target on
   its own. The label is still there for screen readers, in .sr-only. */
.sform__b { flex: none; width: 50px; padding: 0; }

/* ---------- search typeahead ----------
   Beside .sform for the same reason .sform is here: index.html and
   chrome.php share main.css and nothing else that would suit.

   The panel is a child of <body>, not of the form — .hero has
   overflow:hidden and would clip it — so it is painted on the root
   token set rather than .band--dark's. That is deliberate: a light
   panel over the graphite hero is the readable way round, and it
   needs no dark-band variant. */
.sac {
  position: fixed;
  z-index: var(--z-pop);
  max-height: min(60vh, 420px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-2);
  box-shadow: 0 18px 40px -18px rgba(0, 0, 0, .45);
  display: grid;
  align-content: start;
}
.sac__i {
  display: grid;
  gap: 3px;
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--line-soft);
  transition: background var(--dur-1) var(--ease-cut);
}
.sac__i:first-child { border-top: 0; }
.sac__i:hover,
.sac__i[aria-selected="true"] { background: var(--surface-2); }
.sac__k { color: var(--accent); }
.sac__t {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tr-display);
  line-height: calc(var(--lh-tight) + 0.18);
}
/* the two utility rows are sentences, not headings */
.sac__i--all .sac__t,
.sac__i--ask .sac__t {
  font-family: var(--font-body);
  font-size: var(--fs-base);
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
}
.sac__i--ask .sac__t { color: var(--accent); }
